Oracle 12c AFTER INSERT OR UPDATE TRIGGER - sql

I Have a following trigger, which updates but and rolling back automatically, I'm not finding what is the cause, kindly help.
FYI: updating table UDF_DATA has a foreign key reference to CCEX.CUSTOMER triggering table.
CREATE OR REPLACE TRIGGER TR_CUSTOMER_PM
AFTER INSERT OR UPDATE ON CCEX.CUSTOMER FOR EACH ROW
DECLARE
i_subscriber_id Number :=3080;
user_xcep EXCEPTION;
PRAGMA EXCEPTION_INIT( user_xcep, -20001 );
pragma autonomous_transaction;
i_syscode ccex.customer.cust_system_code%type;
BEGIN
IF :new.cust_account_number like 'TID%' THEN
i_syscode:= :new.cust_system_code;
update udf_data set value = 'Term'
where subscriber_id = i_subscriber_id
and cust_system_code = i_syscode
and entity_id = '1488_OTA'
and udf_id = '3994_OTA'
and name = 'Primary Manager';
END IF;
EXCEPTION
when others then
raise user_xcep;
END;
/

Since the trigger is in autonomous_transaction mode, it should close the transaction.
Please add commit just before the exception line.

Related

HI I am trying to prevent inserting the same DATE for the same user. Trigger generates an error stating that it is not mutable

HI I am trying to prevent inserting the same DATE for the same user. Trigger generates an error stating that it is not mutable. Is there any way to fix it?
CREATE OR REPLACE TRIGGER fake_trig
BEFORE INSERT ON newtable
FOR EACH ROW
DECLARE
testtr1 NUMBER;
testtr1 Date;
testtr1 EXCEPTION;
BEGIN
SELECT
CHECKTB1,
CHECKTTB2
INTO
testtr1,
testtr1
FROM newtable
WHERE :NEW.checktb1 = CHECKTB1;
IF :new.checkttb2 = testtr1 THEN
RAISE testtr1;
END IF;
EXCEPTION
WHEN testtr1 THEN
Raise_application_error(-20300,'not working');
WHEN NO_DATA_FOUND THEN
NULL;
END;
A trigger is inappropriate for this task. This is something that should be enforced using a UNIQUE constraint:
ALTER TABLE newtable
ADD CONSTRAINT newtable__checktb1_checkttb2__u UNIQUE (checktb1, checkttb2);
If you did want to use a trigger (don't, this should be handled by a constraint) then all you need to do is fix the variables so they are not all named identically:
CREATE OR REPLACE TRIGGER fake_trig
BEFORE INSERT ON newtable
FOR EACH ROW
DECLARE
testtr1 NUMBER;
testtr2 Date;
testtr3 EXCEPTION;
BEGIN
SELECT CHECKTB1,
CHECKTTB2
INTO testtr1,
testtr2
FROM newtable
WHERE :NEW.checktb1 = CHECKTB1;
IF :new.checkttb2 = testtr2 THEN
RAISE testtr3;
END IF;
EXCEPTION
WHEN testtr3 THEN
Raise_application_error(-20300,'not working');
WHEN NO_DATA_FOUND THEN
NULL;
END;
/
Note: you should give variables meaningful names so that you can tell what they each represent.
db<>fiddle here

How to insert data into table even if trigger fails?

Oracle 11.1
I have custom logging table where I insert data:
CREATE TABLE log_table
(
message VARCHAR2(255),
created_by VARCHAR2(40) NOT NULL,
created_at DATE NOT NULL,
);
I have a trigger that runs on a specific table which does some checkings. My problem is: when the trigger fails, I want to be able to log some data into the log_table.
Trigger:
CREATE OR REPLACE TRIGGER my_trigger
FOR INSERT OR UPDATE OF column
ON my_table
COMPOUND TRIGGER
BEFORE STATEMENT IS
BEGIN
// code
END BEFORE STATEMENT;
BEFORE EACH ROW IS
BEGIN
IF (/*condition for failing*/) THEN
EXECUTE IMMEDIATE 'INSERT INTO mesaj_ama VALUES (:my_message, :my_user, :my_data)'
USING 'custom error message', SYS.LOGIN_USER, SYSDATE;
RAISE_APPLICATION_ERROR(-20001, 'some error');
END IF;
END BEFORE EACH ROW;
END my_trigger;
/
The following code doesn't work. I tried to use EXECUTE IMMEDIATE maybe to force it, but didn't work. I know that in case of an error, there is automatically a table rollback (which means that the INSERT command is cancelled), but I need a way to do this. Any help?
The concept you're looking for is an Autonomous Trasnaction, eg
CREATE OR REPLACE TRIGGER log_sal
BEFORE UPDATE OF salary ON emp FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO log (
log_id,
up_date,
new_sal,
old_sal
)
VALUES (
:old.employee_id,
SYSDATE,
:new.salary,
:old.salary
);
COMMIT;
END;
Yes, PRAGMA AUTONOMOUS_TRANSACTION seems to be the answer. Here is the working code:
Defined a procedure for logging:
CREATE OR REPLACE PROCEDURE log_error(p_error log_table.message % TYPE) AS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO log_table
VALUES (p_error, SYS.LOGIN_USER, SYSDATE);
COMMIT;
END;
and the trigger which calls the procedure:
CREATE OR REPLACE TRIGGER my_trigger
FOR INSERT OR UPDATE OF column
ON my_table
COMPOUND TRIGGER
BEFORE STATEMENT IS
BEGIN
// code
END BEFORE STATEMENT;
BEFORE EACH ROW IS
BEGIN
IF (/*condition for failing*/) THEN
log_error('custom error message');
RAISE_APPLICATION_ERROR(-20001, 'custom error message');
END IF;
END BEFORE EACH ROW;
END my_trigger;
/

Capture log information and raise exception in Oracle Trigger

I have a requirement where the status of the column changes from x to y.
I want to capture which process changed the value from x to y in a log table
and raise an exception to roll back the update using Oracle BEFORE UPDATE trigger
Is this possible using pragma autonomous_transaction ?
Please help.
It's possible. I'm assuming that you know how to identify the "process that changed the value" which I'm guessing is coming from some column in v$session or a user-defined context or some similar source.
You'd normally define a procedure
CREATE OR REPLACE PROCEDURE log_error( <<parameters>> )
AS
PRAGMA autonomous_transaction;
BEGIN
insert into log_table ...
commit;
END;
and then call that procedure from a trigger that throws an exception
CREATE OR REPLACE TRIGGER trg_no_x_to_y
BEFORE UPDATE ON table_name
FOR EACH ROW
BEGIN
IF( :old.column = x and :new.column = y )
THEN
log_error;
raise_application_error( -20001, 'Cannot change column from x to y' );
END IF;
END;

Oracle SQL trigger on update of column

I'm trying to create a trigger for my table which automatically adds a published date based on when a certain flag is set to 'Y'
I don't have much experience with creating triggers but so far this is what i've got
create or replace
TRIGGER ADD_CREATE_DT
after UPDATE of approved ON articles
for each row
BEGIN
:new.create_dt := sysdate where approved = 'Y';
END;
I get this error when updating the column
trigger 'USER.ADD_CREATE_DT' is invalid and failed re-validation
Any ideas?
Thanks
Use the WHEN clause:
create or replace
TRIGGER ADD_CREATE_DT
after UPDATE of approved ON articles
for each row
when (new.approved = 'Y')
BEGIN
:new.create_dt := sysdate;
END;
Or use IF:
create or replace
TRIGGER ADD_CREATE_DT
after UPDATE of approved ON articles
for each row
BEGIN
if :new.approved = 'Y' then
:new.create_dt := sysdate;
end if;
END;
In this case, WHEN is more appropriate and efficient.
create or replace
TRIGGER ADD_CREATE_DT
after UPDATE of approved ON articles
for each row
BEGIN
IF :NEW.approved = 'Y' THEN
:new.create_dt := sysdate;
END IF;
END;
I don't know What version of Oracle do you use?
In Oracle 10g I got the following error:
ORA-04084: cannot change NEW values for this trigger type
04084. 00000 - "cannot change NEW values for this trigger type"
*Cause: New trigger variables can only be changed in before row
insert or update triggers.
*Action: Change the trigger type or remove the variable reference.
It does not allow to change the field on AFTER triggers.

problem with trigger in oracle

the problem is this :
I implemented a trigger on the table called CLAN_AFFILIATI that increases (if inseriemento) and decreases (in case of cancellation) an attribute (NUMAFFILIATI) of another table called CLAN. what I would do is block the update NUMAFFILIATI of Clan by the user and had thought to r another trigge on CLAN that did this:
trigger on CLAN_AFFILIATI(CLAN VARCHAR,AFFILIATO VARCHAR,RUOLO VARCHAR)
CREATE OR REPLACE TRIGGER "AggiornamentoNumAffiliati"
AFTER INSERT OR DELETE ON CLAN_AFFILIATI
FOR EACH ROW
DECLARE
CLAN_APPARTENENZA VARCHAR(20);
BEGIN
IF INSERTING THEN
SELECT NOME INTO CLAN_APPARTENENZA
FROM CLAN
WHERE NOME=:new.CLAN;
UPDATE CLAN
SET NUMAFFILIATI=NUMAFFILIATI+1
WHERE CLAN_APPARTENENZA=NOME;
ELSE
SELECT NOME INTO CLAN_APPARTENENZA
FROM CLAN
WHERE NOME=:old.CLAN;
UPDATE CLAN
SET NUMAFFILIATI=NUMAFFILIATI-1
WHERE CLAN_APPARTENENZA=NOME;
END IF;
END;
trigger on CLAN (NAME VARCHAR ,NUMAFFILIATI INTEGER)
CREATE OR REPLACE TRIGGER "ModificaNumAffiliati"
BEFORE INSERT OR UPDATE OF NUMAFFILIATI ON CLAN
FOR EACH ROW
DECLARE
CONT NUMBER:=0;
BEGIN
IF INSERTING THEN
IF :new.NUMAFFILIATI <> 0 THEN
RAISE_APPLICATION_ERROR(-20016,'NUMERO ERRATO');
END IF;
ELSE
SELECT COUNT(*) INTO CONT
FROM CLAN_AFFILIATI
WHERE :old.NOME=CLAN;
IF CONT <> :new.NUMAFFILIATI THEN
RAISE_APPLICATION_ERROR(-20017,'NUMERO ERRATO');
END IF;
END IF;
END;
but so I'm doing is reporting an error:
error ORA-04091: Table ANTONIO.CLAN_AFFILIATI is being modified, the trigger / function can not read
ORA-06512: at "ANTONIO.ModificaNumAffiliati", line 10
ORA-04088: error during execution of trigger 'ANTONIO.ModificaNumAffiliati'
ORA-06512: at "ANTONIO.AggiornamentoNumAffiliati", line 12
ORA-04088: error during execution of trigger 'ANTONIO.AggiornamentoNumAffiliati
how can I solve this problem ....
This is propably solution:
I tested it with this sample tables:
CREATE TABLE CLAN_AFFILIATI(CLAN VARCHAR2(100),AFFILIATO VARCHAR2(100),RUOLO VARCHAR2(100));
CREATE TABLE CLAN (NOME VARCHAR2(100) ,NUMAFFILIATI NUMBER(10));
You need this helper package.
CREATE OR REPLACE PACKAGE STORE_NOMES
AS
TYPE record_nomes IS RECORD (
nome VARCHAR2(100),
operation VARCHAR2(100) -- insert or delete
);
TYPE array_type_nomes IS TABLE OF record_nomes INDEX BY BINARY_INTEGER;
g_array_nomes array_type_nomes;
END STORE_NOMES;
/
Trigger on CLAN table:
CREATE OR REPLACE TRIGGER MODIFICANUMAFFILIATI
BEFORE INSERT OR UPDATE OF NUMAFFILIATI ON CLAN
FOR EACH ROW
DECLARE
l_CONT NUMBER:=0;
BEGIN
IF INSERTING THEN
-- prevent inserting <> 0
IF :new.NUMAFFILIATI <> 0 THEN
RAISE_APPLICATION_ERROR(-20016,'NUMERO ERRATO');
END IF;
ELSE
SELECT COUNT(*) INTO l_CONT
FROM CLAN_AFFILIATI
WHERE CLAN = :old.NOME;
IF l_CONT <> :new.NUMAFFILIATI THEN
RAISE_APPLICATION_ERROR(-20017,'NUMERO ERRATO');
END IF;
END IF;
END;
/
Before statement trigger on CLAN_AFFILIATI table:
CREATE OR REPLACE TRIGGER TRG_CLAN_AFFILIATI_BEFORE_STMT
BEFORE INSERT OR DELETE
ON CLAN_AFFILIATI
DECLARE
BEGIN
STORE_NOMES.g_array_nomes.DELETE;
END;
/
After statement trigger on CLAN_AFFILIATI table:
CREATE OR REPLACE TRIGGER TRG_CLAN_AFFILIATI_AFTER_STMT
AFTER INSERT OR DELETE
ON CLAN_AFFILIATI
DECLARE
BEGIN
FOR i IN STORE_NOMES.g_array_nomes.FIRST..STORE_NOMES.g_array_nomes.LAST LOOP
IF(STORE_NOMES.g_array_nomes(i).operation = 'INSERTING') THEN
UPDATE CLAN
SET NUMAFFILIATI=NUMAFFILIATI+1
WHERE NOME = STORE_NOMES.g_array_nomes(i).NOME;
ELSIF(STORE_NOMES.g_array_nomes(i).operation = 'DELETING') THEN
UPDATE CLAN
SET NUMAFFILIATI=NUMAFFILIATI-1
WHERE NOME = STORE_NOMES.g_array_nomes(i).NOME;
END IF;
END LOOP;
END;
/
Row Insert/Delete trigger on CLAN_AFFILIATI table:
CREATE OR REPLACE TRIGGER AGGIORNAMENTONUMAFFILIATI
BEFORE INSERT OR DELETE ON CLAN_AFFILIATI
FOR EACH ROW
DECLARE
l_CLAN_APPARTENENZA VARCHAR(20);
BEGIN
IF INSERTING THEN
SELECT NOME INTO l_CLAN_APPARTENENZA
FROM CLAN
WHERE NOME = :new.CLAN;
STORE_NOMES.g_array_nomes(STORE_NOMES.g_array_nomes.COUNT).nome := :new.CLAN;
STORE_NOMES.g_array_nomes(STORE_NOMES.g_array_nomes.LAST).operation := 'INSERTING';
ELSE
SELECT NOME INTO l_CLAN_APPARTENENZA
FROM CLAN
WHERE NOME = :old.CLAN;
STORE_NOMES.g_array_nomes(STORE_NOMES.g_array_nomes.COUNT).nome := :old.CLAN;
STORE_NOMES.g_array_nomes(STORE_NOMES.g_array_nomes.LAST).operation := 'DELETING';
END IF;
END;
/
Now working this (without ORACLE-EXCEPTION):
INSERT INTO CLAN(NOME, NUMAFFILIATI) VALUES('Antonio', 0);
INSERT INTO CLAN_AFFILIATI(CLAN,AFFILIATO,RUOLO) values('Antonio','Affiliato1','Ruolo1');
INSERT INTO CLAN_AFFILIATI(CLAN,AFFILIATO,RUOLO) values('Antonio','Affiliato2','Ruolo2');
Change the first trigger "AggiornamentoNumAffiliati" so that it doesn't immediately try to update clan, but stores the name (NOME) in a PL/SQL-Table within a Package; then, you make an AFTER INSERT OR DELETE (but without the FOR EACH ROW clause) trigger that reads the PL/SQL table from the package and updates the CLANs accordingly.
I don't have my developer tools with me, but it looks to me as though your getting yourself into a cyclic dependency issue of sorts. When your CLAN_AFFILIATI trigger is raised, in it you do an update of CLAN which calls the second trigger, which has a select from the CLAN_AFFILIATI table in the ELSE block.
Maybe the before insert (first query), and after insert(second query) have an affect also.
ORA-04091 is also known as a "mutating table" error - basically, row triggers cannot query or alter the table on which the trigger operates.
#Martin's answer is the classic description of how to work around this issue, but it you're on Oracle 11+ you can use a compound trigger to do the same thing.
Share and enjoy.