subquery in oracle trigger - oracle

I want include condition in my trigger, based on data from a table. As oracle to do allow subquery in trigger, how it can be achived. Please find my code below. Trans_code_master holds the list of valid code, which can be changed.
Thanks in advance for all your help.
CREATE OR REPLACE TRIGGER CUST_TRG
BEFORE INSERT OR UPDATE ON CUST_ALL_TRANS
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
WHEN (NEW.TRANSACTION_CODE IN(SELECT TRANS_CODE FROM TRANS_CODE_MASTER))
BEGIN
INSERT INTO CUST_DEPO_TRANS
(
CUST_ID
,AC_ID
,TRANSACTION_CODE
)
VALUES(
:NEW.CUST_ID
,:NEW.AC_ID
,:NEW.TRANSACTION_CODE
)
EXCEPTION
WHEN OTHERS THEN
-- Consider logging the error and then re-raise
RAISE;
END;
/

Its not possible in write query in when clause. Try the below way
CREATE OR REPLACE TRIGGER CUST_TRG
BEFORE INSERT OR UPDATE ON CUST_ALL_TRANS
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
V_CNT NUMBER;
BEGIN
SELECT COUNT(1) INTO V_CNT
FROM TRANS_CODE_MASTER
WHERE TRANS_CODE = NEW.TRANSACTION_CODE;
IF V_CNT > 0 THEN
INSERT INTO CUST_DEPO_TRANS
(
CUST_ID
,AC_ID
,TRANSACTION_CODE
)
VALUES(
:NEW.CUST_ID
,:NEW.AC_ID
,:NEW.TRANSACTION_CODE
)
END IF;
EXCEPTION
WHEN OTHERS THEN
-- Consider logging the error and then re-raise
RAISE;
END;
/

Related

How to capture errors inside trigger

I want to create trigger which should capture all error whatever it happens inside the trigger.
And it should store in some log table.
Here my challange is how to capture the error occur in DML statments.
THat DML statments error captured in log with respective table name along with column name and error message.
It should also caputre all other errors like no_data_found with exact line number.
Please look at my code below.
If any changes need tell us.
create or replace TRIGGER user_name.sample_trg
AFTER UPDATE ON user_name.transaction_tb
FOR EACH ROW
DECLARE
variable_ln number;
l_err varcha2(4000);
BEGIN
select column_value
into variable_ln
from tb1
where colum_1 = :NEW.colum_1
IF UPDATING THEN
INSERT
INTO hisotry_tb
(
column1,
column2,
column3,
column4,
)
VALUES
(
:NEW.column1,
:NEW.column2,
:NEW.column3,
:NEW.column4,
);
END IF;
IF INSERTING THEN
INSERT
INTO hisotry_tb
(
column5,
column6,
column7,
column8,
)
VALUES
(
:NEW.column5,
:NEW.column6,
:NEW.column7,
:NEW.column8,
);
END IF;
EXCEPTION
WHEN OTHERS THEN
l_err := DBMS_UTILITY.FORMAT_ERROR_STACK || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;
INSERT INTO log (erro_msg, trigger_name, column_name)
VALUES (l_err, 'sample_trg', '?');
DBMS_OUTPUT.put_line (l_err);
END;

how to execute delete statement inside plsql block and call it in a procedure

I have written below pl sql block and trying to create a procedue. But i am getting warnings and not able to execute the procedue.
Please suggest if something i am missing \
Please let me know if this question is duplicate as i am not able to get the exact link to refer
create or replace PROCEDURE EmployeeProc
IS
BEGIN
delete from Employeetable where EmplId in (
select EmployeeId FROM EmployeeMstrTbl where JoiningDate between to_date('2019-01-01','YYYY-MM-DD') and to_date('2019-02-28','YYYY-MM-DD'));
commit;
DBMS_OUTPUT.PUT_LINE('Deleted '||SQL%ROWCOUNT ||' records from Employeetable');
END;
Error: Object Invalid
Try using cursor
CREATE OR REPLACE PROCEDURE EMPLOYEEPROC IS
CURSOR C1 IS
SELECT EMPLOYEEID
FROM EMPLOYEEMSTRTBL
WHERE JOININGDATE BETWEEN TO_DATE('2019-01-01','YYYY-MM-DD') AND TO_DATE('2019-02-28','YYYY-MM-DD'));
BEGIN
FOR I IN C1 LOOP
DELETE FROM EMPLOYEETABLE
WHERE EMPLID=I.EMPLOYEEID;
END LOOP;
COMMIT;
DBMS_OUTPUT.PUT_LINE('DELETED '||SQL%ROWCOUNT ||' RECORDS FROM EMPLOYEETABLE');
END;
Your code works just fine.
CREATE TABLE Employeetable
(
EmplId NUMBER
);
CREATE TABLE EmployeeMstrTbl
(
EmployeeId NUMBER,
JoiningDate DATE
);
CREATE OR REPLACE PROCEDURE EmployeeProc
IS
BEGIN
DELETE FROM Employeetable
WHERE EmplId IN
(SELECT EmployeeId
FROM EmployeeMstrTbl
WHERE JoiningDate BETWEEN TO_DATE ('2019-01-01',
'YYYY-MM-DD')
AND TO_DATE ('2019-02-28',
'YYYY-MM-DD'));
COMMIT;
DBMS_OUTPUT.PUT_LINE (
'Deleted ' || SQL%ROWCOUNT || ' records from Employeetable');
END;
EXEC EmployeeProc;
DROP TABLE Employeetable;
DROP TABLE EmployeeMstrTbl;
DROP PROCEDURE EmployeeProc;
Script output:
Table created.
Table created.
Procedure created.
PL/SQL procedure successfully completed.
Table dropped.
Table dropped.
Procedure dropped.
DBMS Output:
Deleted 0 records from Employeetable
Maybe you have a typo in a table name, column name or something similar.
I suggest that you try to execute your delete statement first to check if it works.
Not sure if perhaps you mistyped something, or if it is to do with how you have it set up. But even if it is a mistype and it could work, it's not nice, so do a loop.
i = 0;
FOR r in (select * FROM EmployeeMstrTbl where JoiningDate between to_date('2019-01-01','YYYY-MM-DD') and to_date('2019-02-28','YYYY-MM-DD'))
LOOP
DELETE FROM Employeetable where EmplId = r.EmployeeId;
END LOOP;
DBMS_OUTPUT.PUT_LINE('Deleted '|| i ||' records from Employeetable');
Because this will work, and more importantly, its easier to understand. Keeping code short and abbreviated has become much less important nowadays since the size of the code is almost never the problem, but keeping it easy to understand is extremely important so that it can be maintained in the future.

How to handle exception within trigger

I want to add exception handling within trigger.
I created Trigger as below.
I want to add exception handling within that. So that my trigger never failed if there are any invalid condition.
CREATE OR REPLACE TRIGGER system_notification_audit
AFTER
INSERT OR
UPDATE
on system_notification
FOR EACH ROW
begin
insert into system_notification_log
select :NEW.ID , :NEW.NAME, :NEW.Description, :NEW.PREFERENCE, :NEW.FREQUENCY,
:NEW.IS_HIGH, :NEW.IS_REQUIRED, :NEW.UPDATED_BY, :NEW.UPDATED_DATE
from dual
where :OLD.PREFERENCE <> :NEW.PREFERENCE
OR :OLD.FREQUENCY <> :NEW.FREQUENCY OR :NEW.IS_HIGH <> :OLD.IS_HIGH OR :NEW.IS_REQUIRED <> :OLD.IS_REQUIRED;
END;
Simply Ignoring an Exception isn't always a good way to design your program. At least log the errors somewhere for later observation.
Note that your INSERT statement can be rewritten using a simple insert ( without a select ) and an IF condition.
CREATE OR REPLACE TRIGGER system_notification_audit AFTER
INSERT OR UPDATE ON system_notification
FOR EACH ROW
BEGIN
IF
:old.preference <>:new.preference OR :old.frequency <>:new.frequency
OR :new.is_high <>:old.is_high OR :new.is_required <>:old.is_required
THEN
INSERT INTO system_notification_log (
id,
name,
description,
preference,
frequency,
is_high,
is_required,
updated_by,
updated_date
) VALUES (
:new.id,
:new.name,
:new.description,
:new.preference,
:new.frequency,
:new.is_high,
:new.is_required,
:new.updated_by,
:new.updated_date
);
END IF;
EXCEPTION WHEN OTHERS THEN
pr_trigger_logs(trig_name => 'system_notification_audit',
err_msg => DBMS_UTILITY.FORMAT_ERROR_BACKTRACE());
-- calling an error logging procedure.
END;
/
I won't give you the definition of procedure pr_trigger_logs. As an exercise, I'll let you come up with it!.

How can I remove last line and user name from my DDL query using regex?

I am using 11GR2
I am trying to see the definition of my trigger but when check my result, I see extra line at end of my trigger ALTER TRIGGER "USER"."EMP" ENABLE ....I dont want to see this line as well as "USER". How can I delete them from my DDL result using regex?
My Query to see definition of trigger without user name:
SELECT REGEXP_REPLACE ( REPLACE ( dbms_metadata.get_ddl ('TRIGGER', 'TRIGGER_NAME'), '""USER"".'),'^\s+', NULL, 1, 0, 'm') FROM dual
Result:
CREATE OR REPLACE TRIGGER "USER"."EMP"
BEFORE INSERT OR UPDATE
of salary
on employee
for each row
declare
v_error VARCHAR2(20);
begin
if :new.salary > 10
then
v_error:=:old.first_name||' cannot have that much!';
raise_application_error(-20999,v_error);
end if;
end;
ALTER TRIGGER "USER"."EMP" ENABLE
Expected Result:
CREATE OR REPLACE TRIGGER "EMP"
BEFORE INSERT OR UPDATE
of salary
on employee
for each row
declare
v_error VARCHAR2(20);
begin
if :new.salary > 10
then
v_error:=:old.first_name||' cannot have that much!';
raise_application_error(-20999,v_error);
end if;
end;
You can try something like this:
SELECT regexp_replace(dbms_metadata.get_ddl('TRIGGER','EMP'),
'(CREATE OR REPLACE TRIGGER )("[A-Z]+"\.)(.+)(ALTER TRIGGER .+)',
'\1\3', 1, 0, 'n')
FROM dual;
Here is a sqlfiddle demo

Oracle scheduled job fails

I am using Oracle 10g and using following script to create the job
CREATE OR REPLACE PROCEDURE archtemp AS
BEGIN
UPDATE ARCH_TEMP SET ARCH_DATE = SYSDATE;
COMMIT;
END archtemp;
VAR jobno NUMBER;
BEGIN
DBMS_JOB.SUBMIT(:jobno, 'archtemp;', SYSDATE, 'sysdate + 1/1440');
COMMIT;
END;
The job never executes automatically (though it runs manually) with following error in alert_sid.log
ORA-12012: error on auto execute of job 26
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at line 8
I am unable to link the ORA-01422 error with any of my code. I'm not doing any fetch here.
Assuming this is a script for SQL*Plus, there are two / misssing, so it does nothing at all:
CREATE OR REPLACE PROCEDURE archtemp AS
BEGIN
UPDATE ARCH_TEMP SET ARCH_DATE = SYSDATE;
COMMIT;
END archtemp;
/
VAR jobno NUMBER;
BEGIN
DBMS_JOB.SUBMIT(:jobno, 'archtemp;', SYSDATE, 'sysdate + 1/1440');
COMMIT;
END;
/
I guess it's another job failing, not yours.
You don't do any data fetch here, but I guess some ON UPDATE trigger on ARCH_TEMP table might. Check it.
I'd use a SERVERERROR trigger (as described here) to try to catch the statement that is failing. But first, you could check the alert log. If recursive SQL is erroring, there may be a problem in the data dictionary.
Try putting in an explicit PL/SQL block as the WHAT parameter.
dbms_job.submit(v_jobno, 'begin archtemp; end;', sysdate, 'sysdate+1/1440');
Here's my test case, which seems to work fine:
create table arch_temp (
arch_date date
);
-- create row to test update
insert into arch_temp (arch_date) values (null);
create or replace procedure archtemp as
begin
update arch_temp set arch_date = sysdate;
commit;
end archtemp;
/
-- test everything works in isoloation
begin
archtemp;
end;
/
select * from arch_temp;
-- arch_date = 10:49:34
select * from user_jobs;
-- no rows returned
declare
v_jobno number;
begin
dbms_job.submit(v_jobno, 'begin archtemp; end;', sysdate, 'sysdate+1/1440');
commit;
dbms_output.put_line('v_jobno: ' || to_char(v_jobno));
end;
/
-- dbms_output...
-- v_jobno: 50520
select * from user_jobs;
-- JOB 50520 returned
-- LAST_DATE = 10:51:11
select * from arch_temp;
-- ARCH_DATE = 10:51:11
I tried solution by Nick Pierpoint as well but it didn't work for me
It looks something is wrong with LUCK because i tried the same thing on another machine having Oracle 9i and it failed!!!
Thank you all for your replies.
Regards

Resources