simple trigger/procedure problem for oracle apex sql - sql

im trying to run this audit trail trigger in oracle apex sql but i keep getting the same error and i dont know what im doing wrong.
also i need to do this same trigger to every table in my database... is there a way i can do the same thing through a procedure so as to only having to do it once?
create or replace TRIGGER AUDIT_TRAIL_USERS_TRIG
-- starts on every update, insert or delete command
AFTER INSERT OR DELETE OR UPDATE ON USERS
FOR EACH ROW
DECLARE
-- variable which declares if update, delete or insert process
v_trg_action varchar2(10);
BEGIN
IF updating THEN
-- when update
v_trg_action := 'UPDATE';
ELSIF deleting THEN
-- when delete
v_trg_action := 'DELETE';
ELSIF inserting THEN
-- when insert
v_trg_action := 'INSERT';
ELSE
-- if something else
END IF;
IF v_trg_action IN ('DELETE','UPDATE','INSERT') THEN
-- if v_trg_action is DELETE, UPDATE OR INSERT then insert old table values
INSERT INTO AUDIT_TRAIL
( AUDIT_USER, AUDIT_DATE, AUDIT_ACTION)
VALUES
(UPPER(v('APP_USER')), SYSDATE, v_trg_action);
ELSE
END IF;
-- about the insert command on the audit table
-- for current apex user: v('APP_USER')
-- for date: SYSDATE
-- for sql command: v_trg_action
END AUDIT_TRAIL_USERS_TRIG;
the error im getting (im sure i have more than what its saying to me)is as follows:
Compilation failed, line 16 (03:29:53) The line numbers associated with compilation errors are relative to the first BEGIN statement. This only affects the compilation of database triggers.
PLS-00103: Encountered the symbol "END" when expecting one of the following: ( begin case declare exit for goto if loop mod null pragma raise return select update while with << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge json_exists json_value json_query json_object json_array Compilation failed, line 25 (03:29:53) The line numbers associated with compilation errors are relative to the first BEGIN statement. This only affects the compilation of database triggers.
PLS-00103: Encountered the symbol "END" when expecting one of the following: ( begin case declare exit for goto if loop mod null pragma raise return select update while with << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge json_exists json_value json_query json_object json_array

IF..ELSE blocks cannot be left empty. If you don't need them remove it, I have added a dummy NULL call for the code to compile.Add appropriate logic as desired, otherwise remove the block.
create or replace TRIGGER AUDIT_TRAIL_USERS_TRIG
-- starts on every update, insert or delete command
AFTER INSERT OR DELETE OR UPDATE ON USERS
FOR EACH ROW
DECLARE
-- variable which declares if update, delete or insert process
v_trg_action varchar2(10);
BEGIN
IF updating THEN
-- when update
v_trg_action := 'UPDATE';
ELSIF deleting THEN
-- when delete
v_trg_action := 'DELETE';
ELSIF inserting THEN
-- when insert
v_trg_action := 'INSERT';
ELSE
-- if something else
NULL;
END IF;
IF v_trg_action IN ('DELETE','UPDATE','INSERT') THEN
-- if v_trg_action is DELETE, UPDATE OR INSERT then insert old table values
INSERT INTO AUDIT_TRAIL
( AUDIT_USER, AUDIT_DATE, AUDIT_ACTION)
VALUES
(UPPER(v('APP_USER')), SYSDATE, v_trg_action);
null;
ELSE
NULL;
END IF;
-- about the insert command on the audit table
-- for current apex user: v('APP_USER')
-- for date: SYSDATE
-- for sql command: v_trg_action
END AUDIT_TRAIL_USERS_TRIG;

Use this code, it will work same like yours
create or replace TRIGGER AUDIT_TRAIL_USERS_TRIG
AFTER INSERT OR DELETE OR UPDATE ON USERS
FOR EACH ROW
DECLARE
BEGIN
IF inserting or updating or deleting THEN
INSERT INTO AUDIT_TRAIL
(AUDIT_USER, AUDIT_DATE, AUDIT_ACTION)
VALUES
(UPPER(v('APP_USER')), SYSDATE, v_trg_action);
END IF;
END AUDIT_TRAIL_USERS_TRIG;

Related

How to include a SUBSELECT in VALUES of INSERT to take values from different row?

I want to make a trigger that will insert a value from a connected row. For example I have a table with 3 rows as below:
I create a trigger that will work once row 3 and 4 are deleted (in this case will be deleted at the same time). And I want to record invnr and extinvnr from row 1 based on idparent=id. I cannot seem to make it work though.
CREATE OR REPLACE TRIGGER LOG_DELETEDPAYMENTS
BEFORE DELETE ON payments
FOR EACH ROW
BEGIN
IF :old.invnr IS NULL THEN
INSERT INTO TABLE_LOG_DELETEDPAYMENTS (table_name, invnr, extinvnr, invdate, transactionid, info, createdby, deleted_by, date_of_delete)
values ('payments', :old.invnr, :old.extinvnr, :old.invdate, :old:transactionid, :old.info, :old.createdby, sys_context('userenv','OS_USER'), SYSDATE);
END IF;
END;
How can I incorporate this into the trigger above?
Try it this way:
create or replace TRIGGER LOG_DELETEDPAYMENTS
BEFORE DELETE ON payments
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
Declare
my_invnr PAYMENTS.INVNR%TYPE;
my_extinvnr PAYMENTS.EXTINVNR%TYPE;
Begin
IF :old.INVNR IS NULL THEN
Select INVNR, EXTINVNR
Into my_invnr, my_extinvnr
From PAYMENTS
Where ID = :old.IDPARENT;
--
INSERT INTO TABLE_LOG_DELETEDPAYMENTS (table_name, invnr, extinvnr, invdate, transactionid, info, createdby, deleted_by, date_of_delete)
values ('payments', my_invnr, my_extinvnr, :old.invdate, :old:transactionid, :old.info, :old.createdby, sys_context('userenv','OS_USER'), SYSDATE);
END IF;
End;
END;
You should select the values of INVNR and EXTINVNR based on ID - IDPARENT relationship and store it in the variables (my_invnr and my_extinvnr).
Those variables are used in INSERT into the log statement.
Because of the Select ... Into statement that is reading the affected table - trigger would fail with table PAYMENTS is mutating error.
To avoid that (to separate transaction from the table) you should Declare the PRAGMA AUTONOMOUS_TRANSACTION.
There will be two rows inserted into LOG as the trigger runs FOR EACH (deleted) ROW.
Regards...
This assumes your are on release 12c or greater of Oracle database.
CREATE OR REPLACE PACKAGE LOG_DELETEDPAYMENTS_PKG
AS
-- need a locally defined type for use in trigger
TYPE t_payments_tbl IS TABLE OF payments%ROWTYPE INDEX BY PLS_INTEGER;
END LOG_DELETEDPAYMENTS_PKG;
CREATE OR REPLACE PACKAGE BODY LOG_DELETEDPAYMENTS_PKG
AS
BEGIN
-- could also put the trigger code here and pass the type as a parameter to a procedure
NULL;
END LOG_DELETEDPAYMENTS_PKG;
CREATE OR REPLACE TRIGGER LOG_DELETEDPAYMENTS_CT
FOR DELETE ON payments
COMPOUND TRIGGER
l_tab LOG_DELETEDPAYMENTS_PKG.t_payments_tbl;
l_count PLS_INTEGER:= 0;
BEFORE EACH ROW IS
BEGIN
-- capture the deletes in local type
l_count := l_count + 1;
l_tab(l_count).invnr := :old.invnr;
l_tab(l_count).extinvnr := :old.extinvnr;
l_tab(l_count).invdate := :old.invdate;
l_tab(l_count).transactionid := :old.transactionid;
l_tab(l_count).info := :old.info;
l_tab(l_count).createdby := :old.createdby;
l_tab(l_count).idparent := :old.idparent;
l_tab(l_count).id := :old.id;
END BEFORE EACH ROW;
AFTER STATEMENT IS
BEGIN
FOR i IN l_tab.first .. l_tab.COUNT LOOP
IF(l_tab(i).invnr IS NULL) THEN
-- if the invoice number is NULL, then get info from parent
SELECT p.invnr
,p.extinvnr
INTO l_tab(i).invnr
,l_tab(i).extinvnr
FROM TABLE(l_tab) p
WHERE p.id = l_tab(i).idparent;
END IF;
END LOOP;
-- log all deletes
FORALL i IN 1 .. l_tab.COUNT
INSERT INTO LOG_DELETEDPAYMENTS
(table_name, invnr, extinvnr, invdate, transactionid, info, createdby, deleted_by, date_of_delete)
VALUES
('payments', l_tab(i).invnr, l_tab(i).extinvnr, l_tab(i).invdate, l_tab(i).transactionid, l_tab(i).info, l_tab(i).createdby, sys_context('userenv','OS_USER'), SYSDATE);
l_tab.delete;
END AFTER STATEMENT;
END LOG_DELETEDPAYMENTS_CT;

Multiple WHEN Statements in Oracle SQL

Can I get some help on below SQL trigger. Something is wrong with using multiple "WHEN" statements.
create or replace TRIGGER TRANS_TASKS_TRIG02
BEFORE INSERT OR UPDATE ON "TASKS"
REFERENCING FOR EACH ROW
WHEN(NEW.STATUS='WIP') BEGIN
IF INSERTING OR UPDATING THEN
:NEW.UPDATED_DATE := NEW_TIME(SYSDATE, 'GMT', 'PDT' );
END IF;
WHEN(NEW.STATUS<>'WIP') BEGIN
IF INSERTING OR UPDATING THEN
:NEW.UPDATED_DATE := NULL;
END IF;
END;
According to Oracle's documentation, you can't have multiple when clauses in a trigger.
You could create to separate triggers:
create or replace TRIGGER TRANS_TASKS_TRIG02_WIP
BEFORE INSERT OR UPDATE ON "TASKS"
REFERENCING FOR EACH ROW
WHEN(NEW.STATUS='WIP') BEGIN
IF INSERTING OR UPDATING THEN
:NEW.UPDATED_DATE := NEW_TIME(SYSDATE, 'GMT', 'PDT' );
END IF;
END;
create or replace TRIGGER TRANS_TASKS_TRIG02_WIP
BEFORE INSERT OR UPDATE ON "TASKS"
REFERENCING FOR EACH ROW
WHEN(NEW.STATUS<>'WIP') BEGIN
IF INSERTING OR UPDATING THEN
:NEW.UPDATED_DATE := NULL;
END IF;
END;
Or have a single trigger with an if statement in it. Note that the if inserting or updating condition is redundant, since the trigger is invoked only before insert or update:
create or replace TRIGGER TRANS_TASKS_TRIG02
BEFORE INSERT OR UPDATE ON "TASKS"
REFERENCING FOR EACH ROW
BEGIN
IF :NEW.STATUS='WIP' THEN
:NEW.UPDATED_DATE := NEW_TIME(SYSDATE, 'GMT', 'PDT' );
ELSIF :NEW.STATUS<>'WIP' THEN
:NEW.UPDATED_DATE := NULL;
END IF;
END;
/

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;
/

Which schema has done insert, update or delete

actually I'm creating a before insert or update or delete trigger which will look on a table IMP_CUSTOMER and log the dataset which was updated, inserted or deleted into a table LOG_IMP_CUSTOMER. Everything work's fine but I just missing one point:
The data in the table can be changed by different database user and I'm trying to get the user which has done the change on the table to log that also into my log table.
This is my trigger until now:
CREATE OR REPLACE TRIGGER TRG_LOG_IMP_CUSTOMER
BEFORE INSERT OR UPDATE OR DELETE ON IMP_CUSTOMER
REFERENCING OLD AS old_buffer NEW AS new_buffer FOR EACH ROW
DECLARE
log_date TIMESTAMP;
sql_type VARCHAR(1);
log_user VARCHAR(10);
BEGIN
-- set log_date
log_date := SYSDATE;
-- set sql_type
IF INSERTING THEN sql_type := 'I';
END IF;
IF UPDATING THEN sql_type := 'U';
END IF;
IF DELETING THEN sql_type := 'D';
END IF;
-- set log_user
log_user := 'USER'; -- hardcoded for test
-- log update and delete
IF UPDATING OR DELETING THEN
INSERT INTO LOG_IMP_CUSTOMER VALUES (:old_buffer.CIF_ID,:old_buffer.PHONE_NUMBER,:old_buffer.PHONE_AREACODE,SEQ_LOG_IMP_CUSTOMER.nextval,log_date,sql_type,log_user);
END IF;
-- log insert
IF INSERTING THEN
INSERT INTO LOG_IMP_CUSTOMER VALUES
(:new_buffer.CIF_ID,:new_buffer.PHONE_NUMBER,:new_buffer.PHONE_AREACODE,SEQ_LOG_IMP_CUSTOMER.nextval,log_date,sql_type,log_user);
END IF;
END;
/
I just searching for any way to set log_user to the user which has done the change.
Some good ideas?
Thanks and regards,
David
you can edit like below.
log_user := sys_context('USERENV','SESSION_USER');

Ora-04072: INVALID TRIGGER TYPE

I'm trying to execute the following SQL statement on Oracle 11g. I'm not experienced when it comes to Oracle and I'm not sure why this is failing. This query was provided to me by our developer.
I was attempting to execute this through the SQL worksheet in OEM.
CREATE OR REPLACE TRIGGER TBL_ADMINCOMMAND_TRG BEFORE
INSERT OR UPDATE ON tbl_AdminCommands FOR EACH ROW
BEGIN
IF inserting
AND :new.ADMINCOMMANDID IS NULL THEN
SELECT TBL_ADMINCOMMANDS_SEQ.nextval INTO :new.ADMINCOMMANDID FROM DUAL;
END IF;
END;
ALTER TRIGGER TBL_ADMINCOMMAND_TRG ENABLE;
The code you show works for me, but only as two separate commands:
1)
CREATE OR REPLACE TRIGGER TBL_ADMINCOMMAND_TRG BEFORE
INSERT OR UPDATE ON tbl_AdminCommands FOR EACH ROW
BEGIN
IF inserting
AND :new.ADMINCOMMANDID IS NULL THEN
SELECT TBL_ADMINCOMMANDS_SEQ.nextval INTO :new.ADMINCOMMANDID FROM DUAL;
END IF;
END;
2)
ALTER TRIGGER TBL_ADMINCOMMAND_TRG ENABLE;
Try doing them one at a time.
As an aside, this line:
SELECT TBL_ADMINCOMMANDS_SEQ.nextval INTO :new.ADMINCOMMANDID FROM DUAL;
can be simplified to this in 11G:
:new.ADMINCOMMANDID := TBL_ADMINCOMMANDS_SEQ.nextval;
In fact, the whole trigger can be simplified to:
CREATE OR REPLACE TRIGGER TBL_ADMINCOMMAND_TRG
BEFORE INSERT ON tbl_AdminCommands
FOR EACH ROW
WHEN (NEW.ADMINCOMMANDID IS NULL)
BEGIN
:new.ADMINCOMMANDID := TBL_ADMINCOMMANDS_SEQ.nextval;
END;
If you are using SQL*Plus, you should end your PL/SQL commands with a single forward slash on a line by itself:
CREATE OR REPLACE TRIGGER TBL_ADMINCOMMAND_TRG
BEFORE INSERT OR UPDATE ON tbl_AdminCommands
FOR EACH ROW
BEGIN
IF inserting AND :new.ADMINCOMMANDID IS NULL
THEN
SELECT TBL_ADMINCOMMANDS_SEQ.nextval
INTO :new.ADMINCOMMANDID
FROM DUAL;
END IF;
END;
/
ALTER TRIGGER TBL_ADMINCOMMAND_TRG ENABLE;
Also note that if your trigger uses IF inserting you could do only a trigger BEFORE INSERT.