I am writing one trigger that will fire on update and insert on one table and insert into another table. Trigger looks like this.
create or replace trigger update_test_history
before insert or update on demo.test_req REFERENCING NEW AS NEW OLD AS old
for each row
declare
v_count number(1);
begin
if :old.quote_request_id != null then
--Update History table
insert into demo.test_req_history
(history_id,
req_id)
values
(isisdba.req_hist_seq.nextval,
:old.req_id);
end if;
end update_test_history;
This trigger is not working on DML. But if i remove the if condition then it start working.
Can someone please tell me whats wrong with this.
Thanks in advance.
null is never equal to any value including null. null is also never unequal to another value including null. So the statement
if( <<some expression>> != null )
then
<<do something>>
end if;
will ever get into the <<do something>> block. Neither will
if( <<some expression>> = null )
then
<<do something>>
end if;
You have to use ternary logic where you are dealing with null values bu saying is null or is not null. It appears that you probably want
if( :old.quote_request_id is not null )
the
<<do something>>
end if;
Related
Hello fellow programmers and happy new year to you all!
I have few university tasks for winter break and one of them is to create trigger on table:
PERSON(ID, Name, Surname, Age);
Trigger is supposed to inform user when they have inserted row with invalid ID. Vadility criteria is that ID is 11 digits long.
I tried to write solution like this:
CREATE OR REPLACE TRIGGER person_id_trigg
AFTER INSERT
ON person
DECLARE
idNew VARCHAR(50);
lengthException EXCEPTION;
BEGIN
SELECT id INTO idNew FROM INSERTED;
IF LENGTH(idNew) <> 11 THEN
RAISE lengthException;
END IF;
EXCEPTION
WHEN lengthException THEN
dbms_output.put_line('ID for new person is INVALID. It must be 11 digits long!');
END;
Then I realized that INSERTED exists only in sqlserver and not in oracle.
What would you suggest I could do to fix that?
Thanks in advance!
Do you want to raise an exception (which would prevent the insert from succeeding)? Or do you want to allow the insert to succeed and write a string to the dbms_output buffer that may or may not exist and may or may not be shown to a human running the insert?
In either case, you'll want this to be a row-level trigger, not a statement-level trigger, so you'll need to add the for each row clause.
CREATE OR REPLACE TRIGGER person_id_trigg
AFTER INSERT
ON person
FOR EACH ROW
If you want to raise an exception
BEGIN
IF( length( :new.id ) <> 11 )
THEN
RAISE_APPLICATION_ERROR( -20001,
'The new ID value must have a length of 11' );
END IF;
END;
If you want to potentially print output but allow the insert to succeed
BEGIN
IF( length( :new.id ) <> 11 )
THEN
dbms_output.put_line( 'The new ID value must have a length of 11' );
END IF;
END;
Of course, in reality, you would never use a trigger for this sort of thing. In the real world, you would use a constraint.
I have following trigger:
create or replace TRIGGER MY_TIGGER_NAME AFTER UPDATE ON MY_TABLE
REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW
WHEN ( NEW.STATUS = ANY (10,40,42,44,46,50,60) and OLD.STATUS != NEW.STATUS)
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
IF :NEW.ALERT is NULL
THEN
dbms_alert.signal('print_update_event','update_message');
ELSE
dbms_alert.signal( :NEW.ALERT,'update_message');
END IF;
commit;
END;
I would like to change it because it send alert for each row. I would like to send only one alert if more than one row with ALERT column equal NULL was updated and I would like to send one alert for each row with ALERT column NOT equal NULL.
As I understand Oracle variables I can declare local variable in my trigger but this variable will be declared separately for each row so following change has no sense:
create or replace TRIGGER MY_TIGGER_NAME AFTER UPDATE ON MY_TABLE
REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW
WHEN ( NEW.STATUS = ANY (10,40,42,44,46,50,60) and OLD.STATUS != NEW.STATUS)
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
flag NUMBER(1,0) :=0;
BEGIN
IF :NEW.ALERT is NULL and flag=0
THEN
dbms_alert.signal('print_update_event','update_message');
flag:=1;
ELSE
dbms_alert.signal( :NEW.ALERT,'update_message');
END IF;
commit;
END;
Package variable could be used instead local variable but I think that package variable is bad idea because two triggers can be executed in parallel. Maybe I am wrong.
I attach diagram of trigger which shows what I would like to achieve.
How to do it?
I have a trigger that has multiple IF statements (not elseif). I did bring the code back down to having twice the same IF statement but only the first IF statement is run.
Is this standard Oracle trigger behavior? one of the if statements is pretty much hard-coded while the other uses a function so there is only a select few cases where both IFs are to be run.
Is there a different way to approach an issue like this? or would the issue be in the trigger code and should i post it here?
the code below has this behavior on our server but is dummied down from the original.
create or replace TRIGGER V_INV_TRANS_BIZTALK
AFTER INSERT ON INVENTORY_TRANSACTION
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
l_type pre_advice_header.user_def_type_4%type;
l_status order_header.status%TYPE;
l_from_loc_zone location.zone_1%TYPE;
l_to_loc_zone location.zone_1%TYPE;
l_patype pre_advice_header.pre_advice_type%type;
l_retour pre_advice_header.user_def_type_6%TYPE;
l_client pre_advice_header.client_id%TYPE;
BEGIN
l_client := :new.client_id;
--Client_id = SD
IF l_client = 'SD'
THEN
CASE
--InBound + Return : Pre_Advice_header
WHEN (:new.code = 'PreAdv Status' and :new.notes in ('In Progress --> Complete')) THEN
select PRE_ADVICE_TYPE
into l_patype
from pre_advice_header
where pre_advice_id = :new.reference_id
and client_id = :new.client_id;
END CASE;
END IF;
--TRANSPARIX
IF l_client = 'SD'
--(beldba.is_transparix_client(p_client_id => :new.client_id) = '1' )
THEN
CASE
--Order is shipped
WHEN (:new.notes like ('%--> Shipped')) THEN
--TRANSEXT
INSERT INTO beldba.biztalk_trigger
(
event_id,
status,
system_id,
client_id,
reference_id,
receiver_id,
user_def_type_1
)
VALUES
(
'TRANSExt',
'Pending',
'DCS',
:new.client_id,
:new.reference_id,
'TRANSPARIX',
''
);
END CASE;
END IF;
COMMIT;
EXCEPTION when others then
NULL;
END;
I think there are two problems here:
If there are no matching cases in the CASE statement, and there is no ELSE clause, the CASE statement will raise an ORA-06592 error 'CASE not found when executing CASE statement'. If you don't want to do anything when there is no matching case, add the following section to your CASE statement:
ELSE
NULL;
You end your trigger with
EXCEPTION when others then
NULL;
This swallows all exceptions, including the one that Oracle was raising to tell you that it couldn't find a CASE to execute. Of course, as this is at the end of your trigger, once your trigger raises an exception no further trigger code gets executed.
EXCEPTION WHEN OTHERS THEN NULL is, quite frankly, a cardinal sin in Oracle. I cannot recommend strongly enough deleting this section of your trigger.
Removing the case statements and replacing with IF statements resolved the issue.
This trigger ends up raising an error regardless of the if statement values. I'm basically selecting from the same table the delete is occurring on and it's not liking it.
CREATE OR REPLACE TRIGGER delete_schedules
AFTER DELETE
ON SCHEDULES
FOR EACH ROW
DECLARE
lParentCond schedules.cond_code%type;
lParentActive schedules.active_flag%type;
lError exception;
BEGIN
if :OLD.thread is not null then
select cond_code, active_flag
into lParentCond, lParentActive
from schedules where schedule_seq = :old.thread;
if lParentCond = 'OK' and lParentActive in ('*', 'F') then
raise lError;
end if;
end if;
EXCEPTION
when lError then
raise;
WHEN OTHERS THEN RAISE;
END delete_schedules;
Any ideas of a workaround?
You're probably getting the dreaded 'MUTATING TABLE' error. Oracle doesn't allow us to fetch data from the table on which the trigger is defined in an AFTER trigger - but in this case you don't need to because the 'old' values are already available. Rewrite your trigger as:
CREATE OR REPLACE TRIGGER delete_schedules
AFTER DELETE
ON SCHEDULES
FOR EACH ROW
BEGIN
if :OLD.thread is not null AND
:OLD.COND_CODE = 'OK' and
:OLD.ACTIVE_FLAG in ('*', 'F')
then
RAISE_APPLICATION_ERROR(-20100, 'Invalid combination of COND_CODE and ACTIVE_FLAG');
end if;
END delete_schedules;
This assumes (based on the use of a singleton SELECT in the question) that there's only one row in SCHEDULES for the given THREAD value. If that's not the case there are other work-arounds, including using a COMPOUND TRIGGER.
Best of luck.
I'm trying to insert a record into a table that's identical to the record that was changed in another table, using a trigger.
I also need to insert whether the operation was an insert, update or delete
CREATE OR REPLACE TRIGGER triggername
AFTER INSERT OR UPDATE OR DELETE
ON tablename
DECLARE
operation_type VARCHAR2;
changed_id VARCHAR2;
BEGIN
IF UPDATING
THEN operation_type := 'Update';
ELSE IF INSERTING
THEN operation_type := 'Insert';
ELSE IF DELETING
THEN operation_type := 'Delete';
END IF;
SELECT id
FROM tablename
WHERE :OLD.record1 != :NEW.record1
OR :OLD.record2 != :NEW.record2
OR :OLD.record3 != :NEW.record3
OR :OLD.record4 != :NEW.record4
OR :OLD.record5 != :NEW.record5
OR :OLD.record6 != :NEW.record6;
INTO changed_id;
INSERT INTO trigger_table.id VALUES(changed_id);
INSERT INTO trigger_table.type VALUES(operation_type);
END;
/
I'm getting an error
"BAD BIND NAME :NEW" or "BAD BIND NAME :OLD" for each of the :NEW or
:OLD
shown above
There are multiple issues with your trigger code:
operation_type VARCHAR2;
You must declare the variable size. else you would get an error
PLS-00215: String length constraints must be in range (1 .. 32767)
For example, modify it as:
operation_type VARCHAR2(20);
changed_id VARCHAR2(20);
SELECT .. INTO statement
SELECT id
FROM tablename
WHERE :OLD.record1 != :NEW.record1
OR :OLD.record2 != :NEW.record2
OR :OLD.record3 != :NEW.record3
OR :OLD.record4 != :NEW.record4
OR :OLD.record5 != :NEW.record5
OR :OLD.record6 != :NEW.record6;
INTO changed_id;
It is syntactically incorrect. INTO clause comes should be between SELECT and FROM clause:
SELECT id
INTO changed_id
FROM tablename
WHERE ...
WHERE :OLD.record1 != :NEW.record1
This is your original question regarding bad bind variable error.
To make reference the :OLD and :NEW values, you need to create ROW LEVEL Trigger which must include the following condition:
FOR EACH ROW
INSERT statements
INSERT INTO trigger_table.id VALUES(changed_id);
INSERT INTO trigger_table.type VALUES(operation_type);
You must insert a single row and not two rows with two insert statements. Also, trigger_table.id is incorrect syntax. You need to mention the column names within parenthesis.
For example,
INSERT INTO trigger_table(column1, column2) VALUES
(changed_id, operation_type);
When you are trying to use :NEW for delete and :OLD for create in trigger, you would see these errors.
It would be better to move the select & insert statements inside
IF INSERTING THEN ... END IF;
IF UPDATING THEN ... END IF;
IF DELETING THEN ... END IF;
to use :NEW and :OLD variables