Need Column name , old value and new value in Trigger - oracle

Our requirement is if any 1 of the column name in table is updating we need to insert the column name in another table so I had written this code
CREATE OR REPLACE TRIGGER Test AFTER
UPDATE ON XX_table
FOR EACH Row
BEGIN FOR C IN
(SELECT column_name
FROM User_Tab_Columns
WHERE Upper(Table_Name) = 'XX_table_name' ORDER BY column_id ASC)
LOOP
IF Updating (c.column_name)
THEN
INSERT INTO Xx_Trigger_table (Rt_Id ,Updated_Column ,updated_status) VALUES(:Old.Rt_Id,C.Column_Name,'Y');
END IF;
END LOOP;
END;
Now Client need Old value as well as new value in the XX_Trigger_Table.I cant Write
INSERT INTO Xx_Trigger_table (Rt_Id ,Updated_Column ,updated_status,old_value, new_value) VALUES(:Old.Rt_Id,C.Column_Name,'Y',:old.c.column_name,:new.c.column_name);
Please suggest me some idea to insert new and old value in the table.
Thanks in Advance.

If you are following this approach , like #Alex Poole suggested you will have to include all the columns that you would like to log. Please see this procedure and the output of it.
CREATE OR REPLACE TRIGGER Test1 AFTER
UPDATE ON TESTEMP
FOR EACH Row
BEGIN FOR C IN
(SELECT column_name
FROM User_Tab_Columns
WHERE Upper(Table_Name) = 'TESTEMP' ORDER BY column_id ASC)
LOOP
IF Updating (c.column_name) then
IF (c.column_name='EMPNO') then
INSERT INTO test_audit (col_name,old_val,new_val,upd_stat) VALUES (C.column_name,:Old.empno,:New.empno,'Y');
ELSIF
(c.column_name='ENAME') then
INSERT INTO test_audit (col_name,old_val,new_val,upd_stat) VALUES (C.column_name,:Old.ENAME,:New.ENAME,'Y');
ELSIF
(c.column_name='MGR') then
INSERT INTO test_audit (col_name,old_val,new_val,upd_stat) VALUES (C.column_name,:Old.MGR,:New.MGR,'Y');
END IF;
END IF;
END LOOP;
END;
SQL> select * from testemp;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
SQL> select * from test_audit;
no rows selected
SQL> update testemp set empno=6677 , ename='JUPITER' where empno=1234;
1 row updated.
SQL> COMMIT;
Commit complete.
SQL> select * from test_audit;
COL_NAME OLD_VAL NEW_VAL U
---------- ---------- ---------- -
EMPNO 1234 6677 Y
ENAME SMITH JUPITER Y

Related

Write a function to find out the hire date of an employee who has joined just prior to the given date passed as input parameter in plsql

how to use ampersand in this program
create or replace function p_hire_date return date is
&v_hire_date employees.hire_date%type;
begin
select hire_date into v_hire_date
from employees
where hire_date < v_hire_date;
return v_hire_date;
end;
error PLS-00103:
Why would you use a substitution variable? This is a function, pass parameter to it and use it in its code.
Example is based on Scott's sample schema; adjust it to your own.
SQL> CREATE OR REPLACE FUNCTION f_hire_date (par_date IN DATE)
2 RETURN DATE
3 IS
4 retval DATE;
5 BEGIN
6 SELECT MAX (a.hiredate)
7 INTO retval
8 FROM emp a
9 WHERE a.hiredate < par_date;
10
11 RETURN retval;
12 END;
13 /
Function created.
SQL>
(You don't have to do that; it's just to know what dates represent):
SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'dd.mm.yyyy';
Session altered.
Sample data:
SQL> SELECT ename, hiredate
2 FROM emp
3 ORDER BY hiredate;
ENAME HIREDATE
---------- ----------
SMITH 17.12.1980
ALLEN 20.02.1981
WARD 22.02.1981
JONES 02.04.1981
BLAKE 01.05.1981
CLARK 09.06.1981
TURNER 08.09.1981
MARTIN 28.09.1981 --> If I pass 30.09.1981, I'll get this date
KING 17.11.1981
JAMES 03.12.1981
FORD 03.12.1981
MILLER 23.01.1982
SCOTT 09.12.1982
ADAMS 12.01.1983
14 rows selected.
Let's try it:
SQL> SELECT f_hire_date (DATE '1981-09-30') FROM DUAL;
F_HIRE_DAT
----------
28.09.1981
SQL>

Simple Stored Procedure for learning

My journey is progressing and I'm learning rapidly. I can't get enough of this stuff... alas I am at a dead end here and need some help.
I am running Visual Studio, am connected to a database (that's filled with dummy data). I am able to run queries on it as expected. However, I'm learning about Procedures right now and I'm coming up with a problem.
I am trying to simply run a query that will select all from table.
CREATE PROCEDURE nearthetop()
BEGIN
SELECT * FROM RESULTS WHERE VOLUME = (SELECT MAX(VOLUME) FROM RESULTS WHERE VOLUME NOT In (SELECT Max(VOLUME) from RESULTS))
END;
When I run this inside Visual Studio I get an error:
EXECUTE FAIL:
CREATE PROCEDURE twofromtop() BEGIN SELECT * FROM RESULTS WHERE VOLUME = (SELECT MAX(VOLUME) FROM RESULTS WHERE VOLUME NOT In (SELECT Max(VOLUME) from RESULTS)) END
Message :
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'END' at line 3
When I remove BEGIN and END; from the procedure it creates it fine.
Why is that?
And then, once created, how do I "call" that procedure so as I can see the returned results?
In Oracle, when PL/SQL code uses a SELECT statement, you have to select into something - a local variable, a collection, whatever - or use a cursor loop and deal with cursor variable.
You're selecting row(s) whose volume value is the 2nd largest. Code you wrote works, but it fetches from the same table 3 times which isn't optimal. For example (based on Scott's sample schema), fetching the 2nd largest salary:
SQL> select ename, sal from emp order by sal desc;
ENAME SAL
---------- ----------
KING 5000 --> largest
FORD 3000 --> Ford and Scott both share
SCOTT 3000 --> the 2nd largest salary
JONES 2975
BLAKE 2850
CLARK 2450
<snip>
Your query returns correct result:
SQL> select *
2 from emp
3 where sal = (select max(sal) from emp
4 where sal not in (select max(sal) from emp)
5 );
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7788 SCOTT ANALYST 7566 09.12.82 3000 20
7902 FORD ANALYST 7566 03.12.81 3000 20
Consider doing it differently:
SQL> select *
2 from (select e.*,
3 rank() over (order by sal desc) rnk
4 from emp e
5 )
6 where rnk = 2;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO RNK
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- ----------
7788 SCOTT ANALYST 7566 09.12.82 3000 20 2
7902 FORD ANALYST 7566 03.12.81 3000 20 2
SQL>
Now, back to your procedure. A simple option which doesn't require much effort is to use a cursor FOR loop. Procedure's IN parameter says which largest salary you want:
SQL> create or replace procedure nearthetop (par_n in number) as
2 begin
3 for cur_r in (select *
4 from (select e.*,
5 rank() over (order by sal desc) rnk
6 from emp e
7 )
8 where rnk = par_n
9 )
10 loop
11 dbms_output.put_line(cur_r.ename ||': '|| cur_r.sal);
12 end loop;
13 end;
14 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> begin
2 nearthetop(2); --> give me the 2nd largest salary
3 end;
4 /
SCOTT: 3000
FORD: 3000
PL/SQL procedure successfully completed.
SQL> begin
2 nearthetop(5); --> give me the 5th largest salary
3 end;
4 /
BLAKE: 2850
PL/SQL procedure successfully completed.
SQL>
I'm just displaying those values on the screen; you never said what you'd want to do with them.
If you'd like to return the result to the caller, a better option is to use a function instead of a procedure. In this case, it would be a ref cursor it returns.
SQL> create or replace function f_nearthetop (par_n in number)
2 return sys_refcursor
3 as
4 rc sys_refcursor;
5 begin
6 open rc for select *
7 from (select e.*,
8 rank() over (order by sal desc) rnk
9 from emp e
10 )
11 where rnk = par_n;
12 return rc;
13 end;
14 /
Function created.
Testing:
SQL> var l_rc refcursor
SQL>
SQL> exec :l_rc := f_nearthetop(2);
PL/SQL procedure successfully completed.
SQL> print :l_rc
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO RNK
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- ----------
7788 SCOTT ANALYST 7566 09.12.82 3000 20 2
7902 FORD ANALYST 7566 03.12.81 3000 20 2
SQL>
So, yes - there are various options. Which one you'll actually use depends on what you want to do.
(As of Visual Studio: I can't help about it, I don't use it.)

How to pass multiple parameters in oracle stored procedure

i am trying to execute like exec print_emp(1010,1111) but it will showing error.
create or replace
procedure print_emp(
P_empno NUMBER
)
IS
begin
for c in ( SELECT *
from emp
where empno in p_empno)
loop
dbms_output.put_line( c.empno||' '||c.ename||' '||c.job||' '||c.sal);
end loop;
END;
You'll need to create a type that is a collection of numbers, and then a procedure that accepts that collection.
Rather than use the IN operator, you should use MEMBER OF to test whether a scalar value is in a collection.
create or replace type tab_number is table of number
/
create or replace procedure print_nums
(p_nums in tab_number)
is
cursor c_main is
select column_value
from table(p_nums)
order by 1;
begin
for r_main in c_main loop
dbms_output.put_line(r_main.column_value);
end loop;
--
if 33 member of p_nums then
dbms_output.put_line('In the list');
end if;
end;
/
exec print_nums(tab_number(10,20,50));
exec print_nums(tab_number(10,20,33));
Obviously, if all parameters you'd like to pass represent the same column value, you can't just list them as if they were two different parameters. If that was the case, procedure should actually name them all, e.g.
create procedure print_emp(par_emp_1 in number, par_emp_2 in number)
but - what if there are 3 or 4 EMPNOs you'd like to pass? You can't modify the procedure every time (not just while declaring it, but also in SELECT statements, as you'd have to add new parameters there as well).
Therefore, one option might be this (as far as I understood the question):
one parameter, whose datatype is varchar2
it means that you'd have to enclose list of EMPNOs into single quotes and separate them with the same separator every time; let it be a comma , sign
cursor's query would contain a subquery (lines #6 - 9) which splits that comma-separated values list of EMPNO values into rows
the rest is simple
Here you go:
SQL> create or replace procedure print_emp(par_empno in varchar2)
2 is
3 begin
4 for c in (select *
5 from emp
6 where empno in (select to_number(regexp_substr(par_empno, '[^,]+', 1, level))
7 from dual
8 connect by level <= regexp_count(par_empno, ',') + 1
9 )
10 )
11 loop
12 dbms_output.put_line(c.empno||' '||c.ename||' '||c.job||' '||c.sal);
13 end loop;
14 end;
15 /
Procedure created.
Scott's EMP sample table:
SQL> select * from emp where deptno = 20;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- ------------------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17.12.1980 00:00:00 1000 20
7566 JONES MANAGER 7839 02.04.1981 00:00:00 2975 20
7788 SCOTT ANALYST 7566 09.12.1982 00:00:00 3000 20
7876 ADAMS CLERK 7788 12.01.1983 00:00:00 1100 20
7902 FORD ANALYST 7566 03.12.1981 00:00:00 3000 20
Testing:
SQL> set serveroutput on
SQL> exec print_emp('7369,7566,7788');
7566 JONES MANAGER 2975
7788 SCOTT ANALYST 3000
7369 SMITH CLERK 1000
PL/SQL procedure successfully completed.
SQL>

CLEAR COLUMNS Definition

I'am confused on this function clear columns. Some one can explain to me this one.
I'am using this in my script.
clear columns
COLUMN temp_in_statement new_value str_in_statement
SELECT DISTINCT
LISTAGG('''' || MONTHCOVERED || ''' AS ' || to_char(MONTHCOVERED,'MONDDYYYY'),',')
WITHIN GROUP (ORDER BY MONTHCOVERED) AS temp_in_statement
FROM (SELECT DISTINCT trunc(MONTHCOVERED) as MONTHCOVERED FROM bbsm_aaa where trunc(MONTHCOVERED) between '01-AUG-19' and '31-OCT-19');
Is there specific column/s that are cleared on this? Or all columns that I assigned was also deleted?
HELP is there for a reason :)
SQL> help clear
CLEAR
-----
Resets or erases the current value or setting for the specified option.
CL[EAR] option ...
where option represents one of the following clauses:
BRE[AKS]
BUFF[ER]
COL[UMNS]
COMP[UTES]
SCR[EEN]
SQL
TIMI[NG]
SQL>
This isn't related to columns' contents, but the way they are formatted in SQL*Plus. Have a look at this example:
First, set some columns' settings using the col command:
SQL> col empno format 9999
SQL> col ename format a5
SQL> col sal format 999g990d00
SQL>
SQL> select empno, ename, sal from emp where rownum < 4;
EMPNO ENAME SAL
----- ----- -----------
7369 SMITH 920,00
7499 ALLEN 1.600,00
7521 WARD 1.250,00
Now, clear those settings and see the difference:
SQL> clear col
columns cleared
SQL> select empno, ename, sal from emp where rownum < 4;
EMPNO ENAME SAL
---------- ---------- ----------
7369 SMITH 920
7499 ALLEN 1600
7521 WARD 1250
SQL>
As you can see, values in those columns weren't affected - only the way they are displayed.

Print a rollback query of a update query using a query

I need to print the rollback query of a update query.
My original query is
UPDATE EMPLOYEE SET NAME = 'SAMAN' WHERE ID=4;
The corresponding update for above query is
UPDATE EMPLOYEE SET NAME=(SELECT NAME FROM EMPLOYEE WHERE ID=4);
I need to print a query to rollback if above original query went wrong. I need it to print it via using a query also.
I'm using oracle 11g database.
You can do it by running a select query like below,
SELECT 'UPDATE EMPLOYEE SET NAME = '''||name||''' WHERE id = '||id||';' FROM employee;
and of course, run the select query before your update query.
As far as I understood the question, it is some kind of a log table you're looking for. I have no idea what you mean by "printing the rollback query"; never heard of anything like that.
So, let me demonstrate what I have on my mind. Review it, apply if it makes sense in your case. The code is commented, I hope you'll understand it
Prepare the scene:
SQL> -- Log table
SQL> create table emp_log
2 (id number constraint pk_el primary key,
3 empno number constraint fk_el_emp references emp (empno) not null,
4 datum date not null,
5 ename varchar2(20)
6 );
Table created.
SQL> -- A sequence which will be used to populate the ID column in the EMP_LOG table
SQL> create sequence seqa;
Sequence created.
SQL> -- Trigger; if new ENAME is different from the last one, log the change (i.e.
SQL> -- store the old ENAME)
SQL> create or replace trigger trg_bu_emp
2 before update on emp
3 for each row
4 begin
5 if :new.ename <> :old.ename then
6 insert into emp_log (id, empno, datum, ename)
7 values (seqa.nextval, :new.empno, sysdate, :old.ename);
8 end if;
9 end;
10 /
Trigger created.
OK, let's see how it works:
SQL> -- Some employees in department 10
SQL> select empno, ename from emp where deptno = 10;
EMPNO ENAME
---------- ----------
7782 CLARK
7839 KING
7934 MILLER
SQL> -- Update KING's name to a better one (just kidding)
SQL> update emp set ename = 'LITTLEFOOT' where empno = 7839;
1 row updated.
SQL> -- What's in the log?
SQL> select * From emp_log order by id desc;
ID EMPNO DATUM ENAME
---------- ---------- ------------------- --------------------
5 7839 30.03.2018 20:22:15 KING
SQL> -- I don't like the new name after all; return the previous one
SQL> update emp e set
2 e.ename = (select l.ename from emp_log l
3 where l.empno = e.empno
4 and l.id = (select max(l1.id) from emp_log l1
5 where l1.empno = l.empno
6 )
7 )
8 where e.empno = 7839;
1 row updated.
SQL> -- What we've done?
SQL> select empno, ename from emp where deptno = 10;
EMPNO ENAME
---------- ----------
7782 CLARK
7839 KING
7934 MILLER
SQL> -- What's in the log now?
SQL> select * From emp_log order by id desc;
ID EMPNO DATUM ENAME
---------- ---------- ------------------- --------------------
6 7839 30.03.2018 20:22:33 LITTLEFOOT
5 7839 30.03.2018 20:22:15 KING
SQL>

Resources