Oracle DB : get the query that triggered a particular update - oracle

I would like to monitor some field in my database ; when an SQL query updates that field with a certain value, I would like to log the query that triggered the update.
How can I do that ?
Thank you in advance!

Good question. Made me curious. I found an answer here.
A LOGMINER utility is also mentioned there. Maybe worth looking into?
SQL> CREATE OR REPLACE FUNCTION cur_sql_txt
2 RETURN CLOB
3 AS
4 v_cnt BINARY_INTEGER;
5 v_sql ORA_NAME_LIST_T;
6 v_rtn CLOB;
7 BEGIN
8 v_cnt := ora_sql_txt (v_sql);
9 FOR l_bit IN 1..v_cnt LOOP
10 v_rtn := v_rtn || RTRIM (v_sql (l_bit), CHR (0));
11 END LOOP;
12 RETURN RTRIM (v_rtn, CHR (10)) || ';';
13 END;
14 /
Function created.
SQL> CREATE OR REPLACE TRIGGER trigger_name
2 BEFORE UPDATE ON emp
3 FOR EACH ROW
4 BEGIN
5 DBMS_OUTPUT.PUT_LINE (cur_sql_txt);
6 END;
7 /
Trigger created.
SQL> SET SERVEROUTPUT ON;
SQL> UPDATE emp
2 SET empno = empno,
3 ename = ename
4 WHERE ROWNUM = 1;
UPDATE emp
SET empno = empno,
ename = ename
WHERE ROWNUM =
:"SYS_B_0";
1 row updated.
SQL>

Why don't you use the audit statement? It allows you to monitor for instance updates against a table.

Related

create a procedure to create a table in Pl/sql from a select statement

I have a select query
select s.site, s.date, s.loaction, s.modified, sysdate p_run_date
from table s
where s.site = 123 --p_site
and s.date >= sysdate -p_date.
i want to create a pl/sql procedure to create a table where p_date and p_site need to be input to the procedure
Why would you want to do that? We normally do NOT create tables dynamically (which is what you'd use in PL/SQL). That procedure would try to create a table with the same name every time you call it (but with different parameters) and fail because table with such a name already exists (as it was created the first time you ran the procedure). Therefore, having a one-time procedure is pretty much useless.
If it must be done, oh well ...
SQL> create or replace procedure p_test (par_deptno in number, par_job in varchar2)
2 is
3 l_str varchar2(200);
4 begin
5 l_str := 'create table my_test as ' ||
6 'select deptno, ename, job, sal ' ||
7 'from emp ' ||
8 'where deptno = ' || dbms_assert.enquote_literal(par_deptno) ||
9 ' and job = ' || dbms_assert.enquote_literal(par_job);
10 execute immediate l_str;
11 end;
12 /
Procedure created.
Testing:
SQL> exec p_test(10, 'CLERK');
PL/SQL procedure successfully completed.
SQL> select * from my_test;
DEPTNO ENAME JOB SAL
---------- ---------- --------- ----------
10 MILLER CLERK 1300
Let's try it again, with different parameters:
SQL> exec p_test(20, 'MANAGER');
BEGIN p_test(20, 'MANAGER'); END;
*
ERROR at line 1:
ORA-00955: name is already used by an existing object
ORA-06512: at "SCOTT.P_TEST", line 10
ORA-06512: at line 1
SQL>
As I said, it'll fail. What you're trying to do is just wrong. You'd rather create a view.

using variable with set of values inside pl/sql cursor

I am trying to use variable v_values with set of values inside cursor in the Where clause using IN operator but it returns no record.
create or replace PROCEDURE MyProc IS
/* Cursor decleration */
CURSOR CUR_DUMMY (v_values as varchar2)
IS
SELECT COL1,COL2,COL3
FROM TABLE
WHERE COL1 IN v_values;
l_values varchar2();
BEGIN
l_values:='(''one'',''two'',''three'')';
FOR REC IN CUR_DUMMY (l_values)
LOOP
dbms.output.put_line(REC.col1 || ' ' || REC.col2 || ' ' || REC.col3);
END LOOP;
END;
Any suggestion how to resolve this issue?
IN operator requires a list of values, not the values as comma delimited string.
One simple solution is using nested tables:
create table tab (col1,col2,col3) as
select 'row'||rownum, 2, 3 from dual connect by level<=10
/
create or replace procedure proc is
cursor cur (vl sys.odciVarchar2List) is
select col1,col2,col3
from tab
where col1 in (select column_value val from table (vl));
begin
for rec in cur (sys.odciVarchar2List ('row1','row3','row9')) loop
dbms_output.put_line (rec.col1||' '||rec.col2||' '||rec.col3);
end loop;
end;
/
SQL> exec proc
row1 2 3
row3 2 3
row9 2 3
Here's an example based on Scott's EMP table (as I don't have yours, whose name is invalid anyway). Besides, you should pay attention to fix obvious errors, such as dbms.output (there's no dot but underline), varchar2() variable with no size, v_values as while declaring the cursor (there's no as) and similar stuff which are easy to fix and show that you're actually paying attention to what you're doing, and not typing just because you must.
Read comments within code.
SQL> set serveroutput on;
SQL> create or replace procedure myproc is
2 cursor cur_dummy (v_values varchar2) is
3 -- split V_VALUES into rows
4 select empno, ename, job
5 from emp
6 where job in (select trim(regexp_substr(v_values, '[^,]+', 1, level))
7 from dual
8 connect by level <= regexp_count(v_values, ',') + 1
9 );
10 l_values varchar2(100);
11 begin
12 -- no need to complicate with single quotes and stuff; just name those values
13 l_values:='CLERK, MANAGER';
14
15 for rec in cur_dummy (l_values)
16 loop
17 dbms_output.put_line(rec.empno || ' ' || rec.ename || ' ' || rec.job);
18 end loop;
19 end;
20 /
Procedure created.
SQL> exec myproc;
7369 SMITH CLERK
7566 JONES MANAGER
7698 BLAKE MANAGER
7876 ADAMS CLERK
7900 JAMES CLERK
PL/SQL procedure successfully completed.
SQL>

How to Escape dot(.) In a like expression in Oracle PL/SQL Statement

In a stored procedure I am filtering out list of employees whose role is SUPER.ADMIN
In such a case I used like expression as below
Emp.Role_nm Like ''''||p_rolenm||''''
p_rolenm I have mentioned in stored procedure as VARCHAR2
When ever I pass a value to p_rolenm as SUPER.ADMIN it's throwing error as identifier SUPER.ADMIN is not declared.
How I can escape . (Dot) in PL/SQL statements?
Why do you use that many single quotes? You don't need any (at least, I think so):
Sample data:
SQL> create table test (id number, role varchar2(20));
Table created.
SQL> insert into test
2 select 1, 'CLERK' from dual union all
3 select 2, 'SUPER.ADMIN' from dual;
2 rows created.
SQL> select * from test;
ID ROLE
---------- --------------------
1 CLERK
2 SUPER.ADMIN
SQL> set serveroutput on;
Procedure (anonymous, though, but that doesn't matter):
SQL> declare
2 p_rolenm varchar2(20) := 'SUPER.ADMIN';
3 l_id number;
4 begin
5 select id into l_id
6 from test
7 where role = p_rolenm;
8
9 dbms_output.put_line('l_id = ' || l_id);
10 end;
11 /
l_id = 2
PL/SQL procedure successfully completed.
SQL>
If you need like, then
SQL> declare
2 p_rolenm varchar2(20) := 'PER.ADM'; --> I changed this ...
3 l_id number;
4 begin
5 select id into l_id
6 from test
7 where role like '%' || p_rolenm || '%'; --> ... and this
8
9 dbms_output.put_line('l_id = ' || l_id);
10 end;
11 /
l_id = 2
PL/SQL procedure successfully completed.
SQL>
If you used dynamic SQL, then
SQL> declare
2 p_rolenm varchar2(20) := 'PER.ADM';
3 l_id number;
4 l_str varchar2(200); --> new variable for execute immediate
5 begin
6 l_str := q'[select id from test where role like '%' || :a || '%']';
7 execute immediate l_str into l_id using p_rolenm;
8
9 dbms_output.put_line('l_id = ' || l_id);
10 end;
11 /
l_id = 2
PL/SQL procedure successfully completed.
SQL>
Shortly, I don't understand what you are doing. Try to follow my examples. If it still doesn't work, post your SQL*Plus session.

How to run a query on different schemes at once

I want to run a query on different schemes to get data and export it. I use the following code
DECLARE
sql_statment VARCHAR2(2000);
BEGIN
FOR c IN (SELECT brchcode FROM brchs) LOOP
sql_statment := 'select distinct ''' || c.brchcode ||''', t.risuid from ' || c.brchcode ||
'.reg_individualacnt_detail t
where t.historytypecode = 60';
EXECUTE IMMEDIATE sql_statment;
END LOOP;
END;
where brchcode is the name of different schemes
I can't see any output. what can I do?
Code you wrote can't work as you have to return the result into something; it is PL/SQL and requires an INTO clause. As you chose to return two values (columns) and multiple rows, that can't be a scalar variable; you could pick ref cursor or a collection, for example.
Here's an example which shows one option.
I'll be using two schemas: SCOTT (current schema) and HR. Both will be having the DEPT table.
As Scott already has it, I'll create one in HR schema and grant access to Scott (otherwise, Scott won't even see it and the procedure (i.e. the function) will fail):
SQL> connect hr/hr
Connected.
SQL> create table dept (deptno number, dname varchar2(10), loc varchar2(10));
Table created.
SQL> insert into dept values (55, 'IT', 'Zagreb');
1 row created.
SQL> grant select on dept to scott;
Grant succeeded.
SQL> commit;
Commit complete.
Back to Scott, to create a table (which contains schema names I'll be selecting from) and a function. I chose to return REF CURSOR; you could return something else, if you want.
SQL> connect scott/tiger
Connected.
SQL> create table brchs (brchcode varchar2(10));
Table created.
SQL> insert into brchs (brchcode)
2 select 'scott' from dual union all
3 select 'hr' from dual;
2 rows created.
SQL> create or replace function f_br
2 return sys_refcursor
3 is
4 l_str varchar2(4000);
5 l_rc sys_refcursor;
6 begin
7 for cur_r in (select brchcode from brchs) loop
8 l_str := l_str ||
9 'union all
10 select ' || chr(39)|| cur_r.brchcode ||chr(39) || ', d.dname
11 from ' || cur_r.brchcode ||'.dept d
12 where d.deptno > 0';
13 end loop;
14
15 l_str := ltrim(l_str, 'union all');
16
17 open l_rc for l_str;
18 return l_rc;
19 end;
20 /
Function created.
SQL>
Finally, testing:
SQL> select f_br from dual;
F_BR
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
'SCOT DNAME
----- --------------
scott ACCOUNTING
scott RESEARCH
scott SALES
scott OPERATIONS
hr IT
SQL>

Passing DataTable to Oracle Procedure

I want to pass DataTable to Oracle procedure which creates a Temporary table.
Even My procedure needs changes to accept datatable as parameter.
I want datatable in place of existing_table.
Below is the Procedure:
CREATE OR REPLACE procedure temptable
is
begin
EXECUTE IMMEDIATE'CREATE GLOBAL TEMPORARY TABLE TEMP_TABLE
ON COMMIT PRESERVE ROWS
AS
select * from existing_table';
End;
How do I work through this. Please help!
Here's an example of how you might do that:
SQL> create or replace procedure temptable (par_table_name in varchar2)
2 is
3 l_cnt number;
4 l_str varchar2(200);
5 begin
6 -- if TEMP_TABLE already exists, drop it
7 select count(*)
8 into l_cnt
9 from user_tables
10 where table_name = 'TEMP_TABLE';
11
12 if l_cnt > 0 then
13 execute immediate 'drop table temp_table';
14 end if;
15
16 -- create a new TEMP_TABLE
17 l_str := 'create global temporary table temp_table on commit preserve rows ' ||
18 'as select * from ' || par_table_name;
19 execute immediate (l_str);
20 end;
21 /
Procedure created.
SQL> exec temptable('dept');
PL/SQL procedure successfully completed.
SQL> select * from temp_table;
DEPTNO DNAME LOC
---------- -------------------- --------------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
However, in Oracle, you don't really create/drop tables dynamically - you create them once and use them many times. For a global temporary table, you'd create it once and populate as necessary in different sessions.
There might be requirements as yours (i.e. to use the same table name for different data sources), so - if that's your case, see if the above code helps.

Resources