create or replace trigger trg_t3 after
insert OR update or delete of salary on t2
for each row
begin
if
to_char(systimestamp,'hh24') not between 9 and 17
then
insert into t3 values (:new.salary, :old.salary, sysdate);
else
DBMS_OUTPUT.put_line ('update is not possible between 9:00 and 17:00');
end if;
end;
/
this will insert old salary ,new salary and the time on table t3 based on the conditions mentioned in trigger on table t2. But i need the name of the employee whose salary is updated or delete.
if i am updating a salary on t2 i need to insert the name of the specific employee whose salary i have modified into t3. But current method will only insert old salary, new salary and time
here is the code for creating the tables
create table t2 ( name varchar(20), salary varchar2(20));
create table t3 (salary_new varchar2(50), salary_old varchar2(20), log_date date);
insert all
into t2 values('hari',2000)
into t2 values('sam',40000)
into t2 values('ravi',60000)
into t2 values('manoj',8000)
into t2 values('pratheep',10000)
into t2 values('john',3000)
into t2 values('joe',50000)
into t2 values('scott',70000)
select * from dual;
Your trigger is marked as after insert or update or delete. You have to handle all three situations otherwise it will not work correctly, because when inserting we have no :old values and when deleting there are no :new.
So if you use :new.name then delete will put null value. If you use :old.name then insert will not work correctly. Use variable or do it like here:
create or replace trigger trg_t3 after
insert or update or delete of salary on t2 for each row
begin
if to_char(systimestamp,'hh24') between 9 and 17 then
dbms_output.put_line ('update is not possible between 9:00 and 17:00');
else
if deleting then
insert into t3 (salary_new, salary_old, name, log_date)
values (null, :old.salary, :old.name, sysdate);
elsif inserting then
insert into t3 (salary_new, salary_old, name, log_date)
values (:new.salary, null, :new.name, sysdate);
elsif updating then
insert into t3 (salary_new, salary_old, name, log_date)
values (:new.salary, :old.salary, :new.name, sysdate);
end if;
end if;
end;
Do an insert select
insert into t3
select emp.name, :new.salary, :old.salary, sysdate
from emp where emp.id = :old.id;
first you need a field to insert the name into so create t3 as
create table t3 (salary_new varchar2(50), salary_old varchar2(20), log_date date, name varchar(20));
then in your trigger simply use
insert into t3 values (:new.salary, :old.salary, sysdate, :new.name);
you could use either :new.name or :old.name as it is not changing so would be the same value.
Related
INSERT AND UPDATE STATEMENTS IN ANYNIMOUS BLOCK SI NOT CALLING TRIGGER BEACAUSE ERROR IN MY TRIG CODE PLEASE HELP ME TO SOLVE MY MISTAKE AS I AM NOOB IN TRIGGER CONCEPT......
Question:
HR Manager wants to keep track of all manager details of every department for auditing in the future. Whenever an HR Manager assigns a new manager, the following manager details should be recorded in DEPT_MANAGER_LOG table.
department_id : department for which manager is getting assigned or getting modified
manager_id : employee_id who is being assigned as manager
start_date : date on which manager is getting assigned for a department
end_date: end_date of manager (when a manager is assigned the end_date will be null)
user_name: name of database user who is doing this modification
Whenever an HR Manager changes manager of any department, the end date of previous manager need to be updated and details of new manager need to be inserted in DEPT_MANAGER_LOG table.'''
create or replace trigger trg_mgr_log
before insert or update of manager_id on departments
for each row
CODE:
create or replace trigger trg_mgr_log
before insert or update of manager_id on departments
for each row
declare
v_dpid departments.department_id%type ;
v_mgr_id departments.manager_id%type ;
v_start_date JOB_HISTORY.START_DATE%type;
v_end_date JOB_HISTORY.END_DATE%type;
begin
/*v_dpid := :new.department_id;
select manager_id into v_mgr_id from departments where department_id = v_dpid;*/
select START_DATE, END_DATE into v_start_date, v_end_date from job_history where employee_id =
v_mgr_id;
if inserting then
v_dpid := :new.department_id;
select manager_id into v_mgr_id from departments where department_id = v_dpid;
if(v_mgr_id is null) then
insert into DEPT_MANAGER_LOG values (v_dpid, :new.manager_id,SYSTIMESTAMP,null,user);
end if;
elsif updating then
v_dpid := :OLD.department_id;
select manager_id into v_mgr_id from departments where department_id = v_dpid;
if(v_mgr_id is not null) then
insert into DEPT_MANAGER_LOG values (v_dpid, :old.manager_id,v_start_date,SYSTIMESTAMP,user);
insert into DEPT_MANAGER_LOG values (v_dpid, :new.manager_id,SYSTIMESTAMP,null,user);
end if;
end if;
end; '
You may be getting a "mutating table" error, because you're selecting from the same table that the trigger is defined on. The following should fix that issue:
create or replace trigger trg_mgr_log
before insert or update of manager_id on departments
for each row
begin
if inserting and
:new.manager_id is null
then
insert into DEPT_MANAGER_LOG values (:new.department_id, :new.manager_id, SYSTIMESTAMP, null, user);
elsif updating and
:OLD.manager_id is not null
then
declare
v_start_date JOB_HISTORY.START_DATE%type;
begin
select START_DATE
into v_start_date
from job_history
where employee_id = :new.manager_id;
insert into DEPT_MANAGER_LOG values (:OLD.department_id, :old.manager_id, v_start_date, SYSTIMESTAMP, user);
insert into DEPT_MANAGER_LOG values (:OLD.department_id, :new.manager_id, SYSTIMESTAMP, null, user);
end;
end if;
end;
I am trying to create a trigger that fires after an insert/update and checks for null values in couple of columns. In case any one of them is NULL, it queries other tables and updates the current table
CREATE OR REPLACE TRIGGER sample_trigger
AFTER INSERT OR UPDATE
ON test_table
FOR EACH ROW
BEGIN
IF :NEW.ID IS NULL OR :NEW.CODE IS NULL
THEN
UPDATE (:NEW.ID,:NEW.CODE) = (SELECT T1.ID,
T2.CODE
FROM TABLE_1 T1
JOIN TABLE_2 T2 ON T1.ID=T2.ID
WHERE ID=:NEW.TEST_ID);
END IF;
END;
/
Warning: Trigger created with compilation errors.
ERROR: PL/SQL: ORA-00928: missing SELECT keyword
You don't UPDATE the :new pseudo-record. Just assign values to it
SELECT t1.id, t2.code
INTO :new.id, :new.code
FROM table1 t1
join table2 t2 on t1.id = t2.id
WHERE id = :new.test_id
A couple additional notes
Your WHERE clause will generate an ambiguous reference error. Since both t1 and t2 have an ID column, you'd need to specify which table's column you are comparing against.
If test_table is the same as either table_1 or table_2, that's going to produce a mutating table exception.
Your trigger should be a BEFORE INSERT since it's modifying data not an AFTER INSERT trigger.
Triggers have to follow syntax as any other program unit; UPDATE you wrote is invalid (obviously), but - you don't need it. Here's what you might have done:
declare two variables which will hold ID and CODE values
set TEST_TABLES's columns values to those variables
CREATE OR REPLACE TRIGGER sample_trigger
AFTER INSERT OR UPDATE
ON test_table
FOR EACH ROW
DECLARE
l_id test_table.id%type;
l_code test_table.code%type;
BEGIN
IF :NEW.ID IS NULL OR :NEW.CODE IS NULL
THEN
SELECT t1.id, t2.code
INTO l_id , l_code
FROM table_1 t1 JOIN table_2 t2 ON t1.id = t2.id
WHERE id = :new.test_id;
:new.id := l_id;
:new.code := l_code;
END IF;
END;
/
My code is something like this below.
DECLARE
empName varchar(20);
BEGIN
FOR emp_details in(SELECT * FROM emp_ids where condition)
LOOP
emp_name(emp_details.id, empName);
dbms_output.putline('The employee ' || empName || ' has id ' || emp_details.id);
END LOOP;
--want to print other details which is returned by select query,
--but it should be outside of loop
END;
Is there a way to use those values which is resulted from the 'for' block outside of the loop-endloop block..
is there something like making it as global, so i can use for block variables across the procedure?
Thanks in advance
DECLARE
empName varchar(20);
BEGIN
FOR emp_details in(SELECT e.id,d.dependent_name FROM emp_table e, dept_table d where e.id=d.id)
LOOP
emp_name(emp_details.dependent_name, empName);
dbms_output.putline('The employee ' || empName || ' has' || emp_details.dependent_name || '(s)depends');
END LOOP;
dbms_output.putline('The employee ' || empName || ' id is' || emp_details.id);
END;
//my select query result would be.
e.id d.dependent_name
1 xxx
1 yyy
To answer your first question, you can't. The variable is scoped to inside the loop, so you can't access it outside. Your alternative is to declare a variable in the outer scope and then store the results from the query and use that instead.
I think this is what you're trying to do, but I don't understand why you want to. It's going to be extremely inefficient since it's going to store the query output in an object and then iterate it twice, but at least the query itself is only is being run once.
CREATE TABLE EMP_TABLE (ID NUMBER);
CREATE TABLE EMP_DETAILS (ID NUMBER, EMP_NAME VARCHAR2(20));
CREATE TABLE DEPT_TABLE (ID NUMBER, DEPENDENT_NAME VARCHAR2(20));
INSERT INTO EMP_TABLE (ID) VALUES (1);
INSERT INTO EMP_TABLE (ID) VALUES (2);
INSERT INTO EMP_TABLE (ID) VALUES (3);
INSERT INTO EMP_DETAILS (ID, EMP_NAME) VALUES (1, 'John');
INSERT INTO EMP_DETAILS (ID, EMP_NAME) VALUES (2, 'Bob');
INSERT INTO EMP_DETAILS (ID, EMP_NAME) VALUES (3, 'Bill');
INSERT INTO DEPT_TABLE (ID, DEPENDENT_NAME) VALUES (1, 'DN1.1');
INSERT INTO DEPT_TABLE (ID, DEPENDENT_NAME) VALUES (1, 'DN1.2');
INSERT INTO DEPT_TABLE (ID, DEPENDENT_NAME) VALUES (1, 'DN1.3');
INSERT INTO DEPT_TABLE (ID, DEPENDENT_NAME) VALUES (2, 'DN2.1');
INSERT INTO DEPT_TABLE (ID, DEPENDENT_NAME) VALUES (2, 'DN2.2');
INSERT INTO DEPT_TABLE (ID, DEPENDENT_NAME) VALUES (3, 'DN3.1');
INSERT INTO DEPT_TABLE (ID, DEPENDENT_NAME) VALUES (3, 'DN3.2');
CREATE OR REPLACE TYPE T_EmpDepRow AS OBJECT
(
ID NUMBER,
EMP_NAME VARCHAR2(20),
DEPENDENT_NAME VARCHAR2(20)
);
/
DECLARE
TYPE T_EmpDeps IS TABLE OF T_EmpDepRow;
V_EmpDeps T_EmpDeps := T_EmpDeps();
BEGIN
SELECT T_EmpDepRow(e.id, ed.emp_name, d.dependent_name)
BULK COLLECT INTO V_EmpDeps
FROM emp_table e
INNER JOIN emp_details ed
ON e.id = ed.id
INNER JOIN dept_table d
ON e.id = d.id;
FOR i IN 1 .. V_EmpDeps.COUNT
LOOP
dbms_output.put_line('The employee ' || V_EmpDeps(i).EMP_NAME || ' has ' ||
V_EmpDeps(i).DEPENDENT_NAME || '(s)depends');
END LOOP;
FOR j IN 1 .. V_EmpDeps.COUNT
LOOP
dbms_output.put_line('The employee ' || V_EmpDeps(j).EMP_NAME || ' id is ' || V_EmpDeps(j).ID);
END LOOP;
END;
/
DROP TABLE EMP_TABLE;
DROP TABLE EMP_DETAILS;
DROP TABLE DEPT_TABLE;
DROP TYPE T_EmpDepRow;
This gives you the output
The employee John has DN1.1(s)depends
The employee John has DN1.2(s)depends
The employee John has DN1.3(s)depends
The employee Bob has DN2.1(s)depends
The employee Bob has DN2.2(s)depends
The employee Bill has DN3.1(s)depends
The employee Bill has DN3.2(s)depends
The employee John id is 1
The employee John id is 1
The employee John id is 1
The employee Bob id is 2
The employee Bob id is 2
The employee Bill id is 3
The employee Bill id is 3
This said, you should really explain why you want to do this in the first place, since this is probably not the right way to go about it.
tables:
employee
dependent
policy_companies
requirement in a procedure:
for cursor(select the details of employees, dependents and policy companies details with condition)
Loop
--print employee details
--print dependent details
--print policy companies details
end loop
Problem is: If an employee has policy in more than one company, it might return more than a row in select query(n times).
so loop is repeated for n times. but i dont want all the details to be repeated n times. I wanted only policy details to be reeated. not others two)
Solution:
I found like create twp cursors with the little modification in the select query.
for cursor1(select the details of employees, dependents and policy companies details)
Loop
--print policy details
end loop
for cursor2(select the details of employees, dependents and policy companies details where conditions like where i will not get more than a row result)
loop
--print dependent and employee details
end loop
I am trying to insert a record into a another table (tableb) once a certain condition is met after an insert into a certain table (tablea)
So i have created a trigger that checks does the above,
condition : after inserting into tablea, check whether the sum of price in tablea is greater than a certain value, if it is, then insert the tino into tableb.
Script below will recreate the issue i am currently facing.. Need another paid of eyes on this.
-- create the tables
CREATE TABLE tablea
(
tino NUMBER not null,
price VARCHAR2(200),
dated date
)
partition by range (DATED)
(
partition PART_201608 values less than (TO_DATE(' 2016-09-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')),
partition PART_201609 values less than (TO_DATE(' 2016-10-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')),
partition PART_201610 values less than (TO_DATE(' 2016-11-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN'))
)
--INSERT VALUES
INSERT INTO tablea (tino,price,dated)VALUES('1234567',10,SYSDATE);
COMMIT;
INSERT INTO tablea (tino,price,dated)VALUES('1234560',20,SYSDATE);
COMMIT;
-- create table table which once condition is met,, data is written into
CREATE TABLE tableb(tino number);
-- CREATE THE TRIGGER
CREATE OR REPLACE TRIGGER trg1
AFTER INSERT
ON tablea
FOR EACH ROW
DECLARE
v_price NUMBER;
v_partition VARCHAR2(20) := 'PART_'||TO_CHAR(SYSDATE,'YYYYMM');
v_tino VARCHAR2(20) := :NEW.tino;
sql_smt VARCHAR2(1000) := '';
BEGIN
sql_smt :='
SELECT SUM(price) price INTO v_price
FROM tablea PARTITION('||v_partition||')
WHERE tino = '||''''||v_tino||''''||'';
BEGIN
EXECUTE IMMEDIATE sql_smt;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
DBMS_OUTPUT.PUT_LINE('QUERY='|| sql_smt);
END;
--DBMS_OUTPUT.PUT_LINE('PRICE =' || v_price);
IF v_price >= 15 THEN
INSERT INTO tableb (tino) VALUES (v_tino);
COMMIT;
END IF;
END;
-- test the trigger
INSERT INTO tablea(tino,price,dated) VALUES('1234567',10,sysdate);
COMMIT;
Should return an ORA-00905: missing keyword
DBMS_OUTPUT.PUT_LINE(SQLERRM);
Returns :
SELECT SUM(price) price INTO v_price
FROM tablea PARTITION(PART_201609)
WHERE tino = '1234567'
Which should ran with no issues
Any pointers??
INTO v_price should be part of the execute immediate statement, not part of the dynamic SQL.
btw I think
WHERE tino = '||''''||v_tino||''''||'';
can be simplified to
WHERE tino = '''||v_tino||'''';
Or even better,
WHERE tino = :tino';
with v_tino passed as a bind variable with something like
execute immediate xyz into v_price using v_tino;
I need to create a before insert trigger on TABLE1 such that it inserts COL2 and COL3 of the new row being inserted in TABLE1 into TABLE2 only when TABLE1.FIELD1 = 'XYZ'. How do I do this so that trigger gets fired only when the condition is met?
CREATE OR REPLACE TRIGGER my_trigger
before insert
ON table_1
FOR EACH ROW
BEGIN
IF :NEW.FIELD1 = 'XYZ'
then
INSERT INTO table_2 (col1, col2) VALUES (:NEW.col1, :NEW.col2);
END IF;
END;
/
or how a_horse_with_no_name noted, your can use the the WHEN clause
CREATE OR REPLACE TRIGGER my_trigger
before insert
ON table_1
FOR EACH ROW
WHEN (NEW.FIELD1 = 'XYZ')
BEGIN
INSERT INTO table_2 (col1, col2) VALUES (:NEW.col1, :NEW.col2);
END;
/