Oracle - update same table on which trigger is fired - oracle

I have a table temp_table with the following columns
Id number,
name varchar,
Password varchar,
pwd_change_date timestamp
I want to capture the timestamp in pwd_change_date column only when password column is changed.
So basically i want to use update statement inside the trigger to update timestamp value in pwd_change_date column for the same record.
Example
When a password is changed for one user, I want to capture the timestamp value in pwd_change_date for the same record.
I tried with before insert and after insert of password on temp_table, but getting mutation error. Is it allowed in Oracle to update the Same row/table on which trigger is fired?

You don't need to update the table again; you can modify the data before it is inserted, with a before-insert row level trigger, e.g.:
create trigger trig_pwd_date
before insert or update on temp_table
for each row
when (old.password is null and new.password is not null or new.password != old.password)
begin
:new.pwd_change_date := systimestamp;
end;
/
db<>fiddle demo
This used the new and old correlation names to decide if the password value has changed; and the new correlation name to assign the system time to the field in the pseudorecord, which becomes the column value when the insert completes.
Hopefully you aren't storing plain-text passwords in your table.

SQL> create table temp_table (password varchar2(50), pwd_change_date TIMESTAMP);
Table created.
SQL> create trigger trig_pwd_date
before insert or update on temp_table
for each row
when (old.password is null and new.password is not null or new.password != old.password)
begin
:new.pwd_change_date := systimestamp;
end; 2 3 4 5 6 7
8 /
Trigger created.

SQL> set time on
15:28:42 SQL> insert into temp_table values ('23456',sysdate);
1 row created.
15:29:01 SQL> commit;
Commit complete.
15:29:09 SQL> select * from temp_table;
PASSWORD
PWD_CHANGE_DATE
12345
21-SEP-20 03.28.02.370377 PM
23456
21-SEP-20 03.29.01.478017 PM

Related

Setting a column on a new row with a trigger

I have a table name dblog where the schema is like
data_balance_id number(8) primary key,
plan_id number(6) not null,
start_date date default current_date,
end_date date not null);
So I am trying to create a trigger which will update enddate column when a insertion is going to happen. enddate will be updated as 30 days from the insertion day. My trigger code is
CREATE OR REPLACE TRIGGER trg
BEFORE INSERT
ON dblog FOR EACH ROW
BEGIN
INSERT INTO dblog (end_date) values (SYSDATE()+30);
END;
/
The insert query is like following
insert into dblog (db_id, planid) values (12,123);
Trigger is created without any error. But at the time of insertion I am getting the following error
insert into dblog (db_id, planid) values (12,123)
*
ERROR at line 1:
ORA-00036: maximum number of recursive SQL levels (50) exceeded
ORA-06512: at "E1038351.TRG1", line 2
You just want to modify the :new pseudo-record. Something like this
CREATE OR REPLACE TRIGGER trg
BEFORE INSERT ON dblog
FOR EACH ROW
BEGIN
:new.end_date := sysdate + 30;
END;
If you don't want end_date to have a time component (or, rather, you want the time component to be midnight), you would want to trunc(sysdate) + 30.
You are trying to insert another row, which also re-triggers the trigger.
In the triggers you have access to the row using variables :NEW and :OLD.
In case when you insert, the :OLD is null , because you doesn't have it in the table yet.
So before inserting row you update his columns like this:
:NEW.END_DATE = SYSDATE+30;

Creating a trigger on Oracle 11g

I need to create a trigger that updates another table whenever the trigger table has an insert into command, or an after insert trigger.
I need to pull the id that's being inserted into the table in order to update the other table, how do I go about doing so?
In case that's confusing, another attempt at my question:
Table 1 has an after insert trigger. Said trigger updates table 2 based on one of the id values being inserted into table 1. How do I pull said id value from table 1 in the trigger?
You can use :new in your trigger to reference the values being inserted, for example
create or replace trigger <trigger_name>
after insert on <table_name>
for each row
declare
l_id number;
begin
select :new.id into l_id from dual;
-- now l_id contains the id of the inserted row, do what you want with it
end;
Don't take the example to literally; you don't have to first select :new.id into a variable, you can use it directly in SQL inside the trigger. I did it here just for illustration.
Take a look at the Oracle docs: Coding Triggers
However, you might also want to take a look at some arguments why you should think twice if you really need to put your logic into triggers: The Trouble with Triggers
CREATE OR REPLACE TRIGGER testempdel
AFTER insert ON testemp
FOR EACH ROW
BEGIN
update testdelvalues set salary=:New.sal;
END;
/
Trigger created.
Initially the salary is null
SQL> select * from testdelvalues;
EMPNO EMPNAME SALARY
---------- ---------- ----------
7369 SMITH
After inserting the value in the other table is updated
SQL> insert into testemp (empno,ename,sal) values (1231,'TESTUSER',1000);
1 row created.
SQL> select * from testdelvalues;
EMPNO EMPNAME SALARY
---------- ---------- ----------
7369 SMITH 1000

Concatenate String with a column in trigger

I have a table called TBL_CAS. In that, FLD_ID as auto increment column and another column is called FLD_CAS_CODE. Now I need to add CAS- as a prefix to FLD_ID and Insert into FLD_CAS_CODE. I need to do this in trigger. I was tried with the below code, But the data in not inserting, What is the problem ?
CREATE OR REPLACE TRIGGER TBL_CAS_TRG
BEFORE INSERT ON TBL_CAS
FOR EACH ROW
BEGIN
:NEW.FLD_CAS_CODE := TO_CHAR ('CAS')||'-'||:NEW.FLD_ID;
END;
I mean `"cas-"+"fld_id"="cas-fld_id"'
You don't need to put TO_CHAR() around things which are already charcater datatypes. But you should cast the numeric identifier (rather than relying on implicit conversion):
:NEW.FLD_CAS_CODE := 'CAS-'||TRIM(TO_CHAR (:NEW.FLD_ID));
which part isn't working exactly? as your trigger seem to work just fine.
SQL> create table TBL_CAS( FLD_ID number, FLD_CAS_CODE varchar2(20));
Table created.
SQL> CREATE OR REPLACE TRIGGER TBL_CAS_TRG
2 BEFORE INSERT ON TBL_CAS
3 FOR EACH ROW
4 BEGIN
5 :NEW.FLD_CAS_CODE := TO_CHAR ('CAS')||'-'||:NEW.FLD_ID;
6 END;
7 /
Trigger created.
SQL> insert into TBL_CAS (fld_id) values (1001);
1 row created.
SQL> select * From TBL_CAS;
FLD_ID FLD_CAS_CODE
---------- --------------------
1001 CAS-1001
SQL>
This will also work fine:
CREATE OR REPLACE TRIGGER TBL_AREA_CODES_TRG
BEFORE INSERT ON TBL_AREA_CODES
FOR EACH ROW
BEGIN
:NEW.OBRM_AREA_CODE := :NEW.STATE_CODE ||'-'||:NEW.DIST_CODE ||'-'||:NEW.CITY_CODE ||'-'||:NEW.AREA_CODE ;
END;

Oracle 'statement level' Trigger

I want to create a Statement level Trigger which means I want to insert only one record into table EMP_AUDIT when 1 or more rows are inserted into table EMP. For example: if I have 10 records inserted into EMP, then only 1 record should be inserted into EMP_AUDIT table.
There are no constraints on columns. (i.e. can be NULL)
I tried to use the following trigger but it gives me the Error(2,2): PL/SQL: SQL Statement ignored
Error(2,14): PL/SQL: ORA-00947: not enough values
CREATE OR REPLACE
TRIGGER TRIG_EMP AFTER INSERT ON EMP
BEGIN
INSERT INTO EMP_AUDIT
VALUES (TRANID,EMPNUM,SYSDATE);
END;
CREATE TABLE EMP
(TRANID NUMBER,
EMPNUM VARCHAR2(100),
EMPLOC VARCHAR2(100));
CREATE TABLE EMP_AUDIT
(EVENTID NUMBER,
EMPNUM VARCHAR2(100),
ENTRDATE DATE);
The statement-level trigger (which you have) cannot see the data that was inserted. After all, if there were 10 rows inserted, what values should the columns be for your audit table?
You need a row-level trigger for this to work, e.g.:
CREATE OR REPLACE
TRIGGER TRIG_EMP
AFTER INSERT ON EMP
FOR EACH ROW
BEGIN
INSERT INTO EMP_AUDIT
VALUES (:NEW.TRANID,:NEW.EMPNUM,:NEW.SYSDATE);
END;
Use this piece of code:
CREATE OR REPLACE TRIGGER
TRIG_EMP
AFTER INSERT ON EMP
FOR EACH ROW
BEGIN
INSERT INTO EMP_AUDIT
VALUES (:NEW.TRANID,:NEW.EMPNUM,:NEW.SYSDATE);
END;

Implicit conversion of date field in a pl/sql trigger?

I need to develop a trigger in PL/SQL (Oracle) before INSERT.
In this table there is a column (cdat) of type DATE.
Let's say i do INSERT INTO myTbl (123,'12/05/2011');
In my trigger the :NEW.CDAT is converted in the final date system or it's still a varchar?
Do I need to do a TO_DATE(:NEW.CDAT) to get the date value?
:NEW.CDAT will be a date. The :new and :old variables in triggers are always the type of the destination field.
I wasn't able to find anything in the Oracle documentation that confirms my statement, but I was able to devise some experimental proof:
CREATE TABLE test2 (a DATE);
CREATE OR REPLACE TRIGGER bu_test2
BEFORE INSERT OR UPDATE
ON test2
FOR EACH ROW
DECLARE
PROCEDURE type_test(in_type DATE) IS
BEGIN
DBMS_OUTPUT.put_line('date');
END;
PROCEDURE type_test(in_type VARCHAR2) IS
BEGIN
DBMS_OUTPUT.put_line('varchar2');
END;
BEGIN
type_test(:new.a);
END;
INSERT INTO test2
VALUES ('24-Mar-2011');
Since type_test is overloaded, Oracle will choose which procedure to use based on the type being passed in. The results of this script are:
Table created.
Trigger created.
date
1 row created.
You need to do a conversion if your session parameter 'NLS_DATE_FORMAT' is not 'mm/dd/yyyy'.
For example:
create table myTbl (id number, cdat date);
select *
from nls_session_parameters ns
where ns.parameter = 'NLS_DATE_FORMAT';
PARAMETER VALUE
-------------------------------------------------------
NLS_DATE_FORMAT DD-MON-RR
In this case, without a to_Date you'll get an error:
insert into myTbl
values
(123, '12/05/2011');
ORA-01843: not a valid month
You can change this paramter at session level, system level, etc.
zep#dev> alter session set NLS_DATE_FORMAT = 'mm/dd/yyyy';
Session altered
select *
from nls_session_parameters ns
where ns.parameter = 'NLS_DATE_FORMAT';
PARAMETER VALUE
-------------------------------------------------------------------------------------
NLS_DATE_FORMAT mm/dd/yyyy
insert into myTbl
(id, cdat)
values
(123, '12/05/2011');
1 row inserted
zep#dev> select *
2 from myTbl;
ID CDAT
---------- -----------
123 05/12/2011
Test on a row level trigger
truncate table Mytbl;
alter session set NLS_DATE_FORMAT = 'DD-MON-RR';
create or replace trigger befins_myTbl
before insert on myTbl
for each row
declare
begin
-- demo
:new.cdat := :new.cdat + numtoyminterval(1,'YEAR');-- (demo trigger add 1 year )
end;
insert into myTbl
(id, cdat)
values
(123, '12/05/2011');
Output: ORA-01843: not a valid month
alter session set NLS_DATE_FORMAT = 'mm/dd/yyyy';
insert into myTbl
(id, cdat)
values
(123, '12/05/2011');
commit;
select *
from myTbl;
Output
ID CDAT
---------- -----------
123 12/05/2012

Resources