I have try to create procedure for updating row in table 'Departments'. I have to check if department name is unique. If is not unique it has to be stored in new table 'ERROR_DEPART'. I have tried to do that with exception but I fail to execute code.
CREATE PROCEDURE UPD_DEPARTMENT IS
v_depid department.department_id%TYPE;
v_depn department.department_name%TYPE;
v_lid department.location_id%TYPE;
v_phn employees.phone_number%TYPE;
BEGIN
select distinct departments.department_name, locations.location_id, employees.phone_number
into v_depn, v_lid, v_phn
from departments
inner join locations
on departments.location_id = locations.location_id
inner join employees
on departments.manager_id = employees.employee_id
END;
I'd suggest a unique index on department name column. Doing so, Oracle won't let you update any department name so that it would make a duplicate.
Here's an example.
First, test case, based on Scott's DEPT table:
SQL> create table depart as select * From dept;
Table created.
SQL> create unique index ui1_dept on depart (dname);
Index created.
SQL> create table error_depart as select * From depart where 1 = 2;
Table created.
SQL>
The procedure - in the exception handler section - inserts a row into the ERROR_DEPART table if uniqueness is violated.
SQL> create or replace procedure p_upd_dept (par_deptno in number, par_dname in varchar2)
2 is
3 begin
4 update depart d set
5 d.dname = par_dname
6 where d.deptno = par_deptno;
7
8 exception
9 when dup_val_on_index then
10 insert into error_Depart(deptno, dname) values (par_Deptno, par_dname);
11 end;
12 /
Procedure created.
SQL>
Testing: First, what we have now?
SQL> select * From depart;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> begin
2 p_upd_dept(10, 'SALES'); --> a duplicate
3 p_upd_dept(20, 'new dept'); --> not a duplicate
4 end;
5 /
PL/SQL procedure successfully completed.
SQL> select * from depart;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 new dept DALLAS --> not a duplicate - updated
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> select * From error_depart;
DEPTNO DNAME LOC
---------- -------------- -------------
10 SALES --> duplicate
SQL>
Related
I am new to PL-SQL. Please can someone help me on wrapping the below.
I need to:
Copy contents of table A and store in global temp table
Do an update on Table A & other tasks
Revert contents of Table A from global temp table
Delete temp table
ANy help is much appreciated.
Here you are:
Sample tables:
SQL> create table test as select * from dept;
Table created.
SQL> create global temporary table gtt_test as
2 select * from test where 1 = 2;
Table created.
SQL> select * from test;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
1 Dept 1 NY
2 Dept 2 London
6 rows selected.
SQL> select * from gtt_test;
no rows selected
Anonymous PL/SQL block which does what you asked:
SQL> begin
2 -- copy contents ...
3 insert into gtt_test select * from test;
4
5 -- update A
6 update test set loc = 'blabla';
7 delete from test where deptno = 20;
8
9 -- revert
10 delete from test;
11 insert into test select * from gtt_test;
12
13 -- delete temp
14 delete from gtt_test;
15 end;
16 /
PL/SQL procedure successfully completed.
At the end, no changes whatsoever:
SQL> select * from test;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
1 Dept 1 NY
2 Dept 2 London
6 rows selected.
SQL> select * from gtt_test;
no rows selected
SQL>
[EDIT]
Query you posted as a comment isn't correct; dynamic SQL must be enclosed into single quotes, it must not be terminated by a semi-colon while all other statements must. Fixed, it should be
BEGIN
EXECUTE IMMEDIATE 'create table camel_route_temp as '
|| ' select * from acquire.CAMEL_ROUTE_PROCESS';
DBMS_OUTPUT.put_line ('Temp table created');
--update A
UPDATE acquire.camel_route_process
SET acquire = 'DISABLED',
assure = 'DISABLED',
validation = 'DISABLED',
report = 'DISABLED',
notification = 'DISABLED';
COMMIT;
DBMS_OUTPUT.put_line ('Aquire components disabled');
END;
/
Create a PL/SQL block to insert a new record into the Department table. Fetch the maximum department id from the Department table and add 10 to it
I can't see images, but - here's how I understood your homework:
Current table contents:
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------------- --------------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
Procedure:
SQL> create or replace procedure p_ins_dept (par_dname in varchar2, par_loc in varchar2) is
2 l_deptno number;
3 begin
4 select max(deptno)
5 into l_deptno
6 from dept;
7
8 insert into dept (deptno, dname, loc)
9 values (nvl(l_deptno, 0) + 10, par_dname, par_loc);
10 end;
11 /
Procedure created.
Testing:
SQL> exec p_ins_dept ('Test', 'Zagreb');
PL/SQL procedure successfully completed.
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------------- --------------------
50 Test Zagreb --> here it is
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
Note that such an approach is likely to fail in a multi-user environment where
two (or more) users simultaneously call the procedure,
fetch MAX department number value
add 10 to it
insert a row
which fails for the 2nd (and other) user because primary key is violated because of the 1st user - who successfully inserted the row
But OK; for a simple homework question, I guess you don't care much about such things.
I want to create a trigger that just inserts the name of the modified table and the operation performed on it. Kind of like:
BEGIN
INSERT INTO audit_table VALUES(user,tg_name,tg_op);
END;
However I can't find what's the equivalent in Oracle to tg_name and tg_op from Postgres.
Huh, there's nothing that simple in Oracle, as far as I can tell (but I admit - I may be very wrong and someone might know better). Anyway, until that better answer comes, here's a little bit of coding; see if it helps.
I'm creating a test table and a log table (though, I'm not sure how good your log table actually is; you don't know when something was done etc. so - I took some freedom to add at least the DATE column).
SQL> create table test as select * From dept;
Table created.
SQL> create table test_log
2 (username varchar2(30),
3 c_date date,
4 table_name varchar2(30),
5 action varchar2(1));
Table created.
A trigger: unfortunately, I don't know whether there's a built-in function which returns name of an object (a table, in our case) you're doing something with, so I'm querying USER_OBJECTS.
SQL> create or replace trigger trg_test_log
2 after insert or update or delete on test
3 for each row
4 declare
5 l_table_name varchar2(30);
6 l_action varchar2(1);
7 begin
8 select object_name
9 into l_table_name
10 from user_objects
11 where object_id = dbms_rowid.rowid_object(nvl(:new.rowid, :old.rowid));
12
13 if inserting then
14 l_action := 'I';
15 elsif updating then
16 l_action := 'U';
17 elsif deleting then
18 l_action := 'D';
19 end if;
20
21 insert into test_log (username, c_date, table_name, action)
22 values (user, sysdate, l_table_name, l_action);
23 end;
24 /
Trigger created.
And ... action!
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> select * From test;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> delete from test where deptno = 40;
1 row deleted.
SQL> update test set loc = 'CROATIA' where deptno = 10;
1 row updated.
SQL> insert into test (deptno, dname, loc) values (99, 'STACK', 'OVERFLOW');
1 row created.
SQL> select * From test;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING CROATIA
20 RESEARCH DALLAS
30 SALES CHICAGO
99 STACK OVERFLOW
SQL> select * From test_log;
USERNAME C_DATE TABLE_NAME A
------------------------------ ------------------- ------------------------------ -
SCOTT 23.09.2018 08:44:17 TEST D
SCOTT 23.09.2018 08:44:17 TEST U
SCOTT 23.09.2018 08:44:17 TEST I
SQL>
There is no point in having special attribute for table_name and operation in DML trigger.
DML trigger can be created only on ONE table so you can hardcode its name in trigger itself.
Conditional predicates INSERTING, DELETING, and UPDATING can be used to derive operation name.
If, howevere, you want to create schema level trigger then you can use predefined attributes ora_*.
create table audit_table
(
user_name varchar2(30),
table_name varchar2(30),
action varchar2(30)
);
create or replace trigger schema_trigger
before create or alter
on schema
begin
insert into audit_table
(user_name, table_name, action)
values
(ora_login_user, ora_dict_obj_name, ora_sysevent);
end schema_trigger;
/
PL/SQL Triggers
I wanted to if it is possible to detect changes in Oracle DB table for each column and its value and capture the change is a separate temporary table ?
Yes; people usually do that using triggers.
Here's a simple example:
SQL> create table dept_log
2 (deptno number,
3 dname varchar2(20),
4 loc varchar2(20),
5 when date
6 );
Table created.
SQL>
SQL> create or replace trigger trg_bu_dept
2 before update on dept
3 for each row
4 begin
5 if :new.dname <> :old.dname or
6 :new.loc <> :old.loc
7 then
8 insert into dept_log (deptno, dname, loc, when)
9 values (:new.deptno, :old.dname, :old.loc, sysdate);
10 end if;
11 end;
12 /
Trigger created.
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> update dept set loc = 'LONDON' where deptno = 40;
1 row updated.
SQL> select * From dept_log;
DEPTNO DNAME LOC WHEN
---------- -------------------- -------------------- -------------------
40 OPERATIONS BOSTON 11.04.2018 22:00:23
SQL>
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>