Skip to Main Content

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Sai.

Asked: November 29, 2004 - 8:14 pm UTC

Last updated: August 04, 2017 - 6:06 am UTC

Version: 8.1.7.4

Viewed 50K+ times! This question is

You Asked

Tom,

Most of the times it appear that either before or after row trigger can be used to serve the purpose in real world applications. Under what circumstances before and after trigger needs to be used.

As per this note: </code> http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:11504247549852, <code>

A trigger can be fired more than once for the same row update from one sql statement. How can we make sure that it is fired only once for each row in an update statement other than using "After trigger for statement"?

Thanks for your time.

and Connor said...

a before trigger can modify the :new values.

you can have many before triggers -- each modifying the :new values.

ONLY the after for each row can look at a "stable" value in the :new record. So, if you doing data validation (eg: this column must be between 1 and 30 when this condition is true), you should do that in an AFTER trigger because the BEFORE triggers may change the value on you (and since BEFORE triggers fire in SOME RANDOM order -- you cannot be assured that your BEFORE trigger fires before or after some other BEFORE trigger)

So, use BEFORE FOR EACH row when you need to WRITE to the :new record
use AFTER FOR EACH row triggers when you want to VALIDATE the final values
in the :new record

That entire referenced thread was the proof that you cannot be sure the trigger is fired only once for each row! So, confused by your request there.

Addenda 2017
============
The statement

"and since BEFORE triggers fire in SOME RANDOM order -- you cannot be assured that your BEFORE trigger fires before or after some other BEFORE trigger"

is no longer true in recent versions of Oracle, the FOLLOWS clause allows you to define a firing order.

An example of that is here

https://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:9526909800346345473

Rating

  (52 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Excellent answer.

Sai, November 30, 2004 - 2:09 pm UTC

Tom,

Another related question, you said:
"That entire referenced thread was the proof that you cannot be sure the trigger
is fired only once for each row! So, confused by your request there."

What if we have a trigger for each row and write rowid along with some unique transaction id, by using a sequence, to a logging table and have after trigger for statement update that logging table rows where the for each row trigger was fired more than once. For example, if the triggeer for each row is being used to send out e-mail, by using the above approach we can make sure that actual e-mail was sent out only once. Does this work, or are there any better options?

Thanks.

Tom Kyte
November 30, 2004 - 8:07 pm UTC

dbms_job

use things in triggers that are transactional. instead of a triggre like:


begin
send_mail( ... );
end;


call dbms_job and ask it to send the mail right after you commit. If you roll back (or get rolled back), it'll un-queue the job and no mail will be sent.

After and Before Trigger Fire Order

Dennis, January 04, 2005 - 8:21 am UTC

Hi Tom,

How to find the trigger order? Is there any way we can order trigger execution OR Oracle has a predefined order which we can predict? Thanks a lot for your help.(something sp_settriggerorder in SQL Server)

CREATE TABLE dtemp
(A Varchar2(50));

CREATE TABLE dtemp2
(A Number,
B Varchar2(50));

CREATE OR REPLACE PAKAGE ABC IS
A INTEGER :=1;
END

set serveroutput on
begin
 abc.a:=1;
end;
/

CREATE OR REPLACE TRIGGER scott.before_row_insert1
BEFORE INSERT
ON scott.dtemp
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
Begin
 insert into dtemp2 values(abc.a,'before row insert1');
 abc.a := abc.a + 1;
End;
/

CREATE OR REPLACE TRIGGER scott.before_row_insert2
BEFORE INSERT
ON scott.dtemp
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
Begin
 insert into dtemp2 values(abc.a,'before row insert2');
 abc.a := abc.a + 1;
End;
/

CREATE OR REPLACE TRIGGER scott.after_row_insert1
AFTER INSERT
ON scott.dtemp
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
Begin
 insert into dtemp2 values(abc.a,'after row insert1');
 abc.a := abc.a + 1;
End;
/

CREATE OR REPLACE TRIGGER scott.after_row_insert2
AFTER INSERT
ON scott.dtemp
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
Begin
 insert into dtemp2 values(abc.a,'after row insert2');
 abc.a := abc.a + 1;
End;
/

SQL>insert into dtemp values('mygod');




 

Tom Kyte
January 04, 2005 - 8:50 am UTC

the only thing you can be sure of is:

all BEFORE triggers fire in some order
loop for each row modified
before triggers will fire in some order
after triggers will fire in some order
end loop
all AFTER triggers fire in some order



in some order. any order we feel like. it is documented to be the case.


IF there is some explicit order they should fire in, DO NOT put them into separate triggers (that would not be logical even - if something has a linear order of progression, we should have some linear piece of procedural code to do it).

Me, I would use a single trigger of each type AT MOST and have that trigger call a packaged procedure (eg: each trigger is pretty small -- one line, passing inputs/outputs to a packaged procedure)

Modifying old values

APL, March 02, 2005 - 1:41 am UTC

Can we modify the old values in a row using before triggers?
I mean :old.col1=old.col2;
I want to update my old column values with old column value from another column in the same table?

Tom Kyte
March 02, 2005 - 7:31 am UTC

I sat and stared at this for a while....

but I cannot imagine any possible reason why this would mean "anything"?

what are you trying to accomplish via this? what is the goal here.

Triggers

APL, March 04, 2005 - 2:09 am UTC

Sorry It was new value. Sorry for the trouble.

Trigger order

Sonali, May 17, 2005 - 9:55 am UTC

I have 2 'before insert' triggers. They need to be run in certain order. I read this link and you suggested to use some package do it. Do you have a small example of how to do it ? I will appreciate it.

Thanks

Tom Kyte
May 17, 2005 - 11:20 am UTC

create trigger the_only_trigger_that_should_be_there
before insert
begin
package.procedure1_with_code_that_was_in_trigger_one_before;
package.procedure2_with_code_that_was_in_trigger_one_before;
end;
/

concept is to have a single trigger that calls the code in the order you need it to

Before or After : one or Two Triggers

A reader, July 01, 2005 - 12:21 am UTC

We have a requirement to populate history tables.

The parent table already has a Before Insert or Update trigger to insert the PK sequence value and the create date,last update date columns.

Do I create an AFTER INSERT OR UPDATE TRIGGER to load data
into History table
or combine the logic in the before Insert or Update trigger.
I was thinking technically creating history records should be done after the parent table is populated so its best to do that in an AFTER trigger.But this will make 2 triggers firing.

How much is it of a performance gain if any to have one Before Insert update trigger rather than having
2 triggers one Before I U and another After I U.

What are your thoughts ?



Tom Kyte
July 01, 2005 - 9:52 am UTC

well, if you only care about the :OLD values, it is safe to put into the before trigger.

suggestion: put all SQL you think about putting into triggers in a packaged procedure and call that procedure from the trigger -- better caching of SQL that way.

I consider old as well as new

A reader, July 01, 2005 - 11:02 am UTC

Thanx for your response.
I consider old as well as new values.

:New values for Inserts/Updates

:OLD values for Deletes.

This way the history table is complete , I dont need to query the parent table for the latest record.

So what Trigger would be in this case. ?
Still put it in along with B IU trg or put the history in A IU.


Tom Kyte
July 01, 2005 - 12:03 pm UTC

it should be an after trigger, since someone can come along next year, add a new BEFORE FOR EACH ROW trigger that fires after (or before, you cannot control this) your BEFORE FOR EACH ROW trigger and changes the :new values -- making the ones you recorded "wrong"

Question about your comment just aobve this

A reader, July 03, 2005 - 11:43 am UTC

Thanx Understood your point. One Question
"since someone can come along next year, add a new
BEFORE FOR EACH ROW trigger"

I would add any new Trigger logic to the existing BEFORE Trigger.
What would be a situation when we would need more than one
BEFORE I U Trigger.

Thanx



Tom Kyte
July 03, 2005 - 12:09 pm UTC

You would

but would I?
will they?

that YOU would not is not relevant.
That SOMEONE ELSE can is.

update on same table using Trigger

Hawk, August 02, 2005 - 4:22 pm UTC

Tom,
I need to write a trigger which will update a sysdate on value column in a row whenever there is an update
e.g

table1 (id number, date date, value varchar(10))

I am just trying to track whenever value column changes I should update the date column to sysdate and only that row should update 'id' column is unique

create or replace triggers abc
after update or insert on table1
begin
date = sysdate;
end abc;

I know the above is wrong but please advise

Thankyou very much.

Tom Kyte
August 02, 2005 - 4:40 pm UTC

....
begin
:new.date_field := sysdate;
end;
/

You might be interested in the application developers guide:
</code> http://docs.oracle.com/docs/cd/B10501_01/appdev.920/a96590/adg13trg.htm#376 <code>

be careful

Andrew, August 02, 2005 - 5:26 pm UTC

It does not mean any values changed just because the update trigger fires. ie 'update table1 set col1 = col1' - check that the old and new values are different - preferably in the when clause.

I get an error

Hawk, August 02, 2005 - 5:37 pm UTC

ORA-04082: NEW or OLD references not allowed in table level triggers




Tom Kyte
August 02, 2005 - 7:24 pm UTC

did you read the referenced chapter, it'll REALLY (honest!) help you program TRIGGERS (for it is, well, a chapter on programing triggers)

you do not want a table trigger, you want a row trigger so you can write to the :NEW record.

CAST not allowed

VA, August 09, 2005 - 10:51 am UTC

Oracle 8.1.7.4

SQL> create table t(i varchar2(10));

Table created.

SQL> drop sequence s;                                                       

Sequence dropped.

SQL> create sequence s;

Sequence created.

SQL> create or replace trigger trg before insert or update on t for each row
  2  begin
  3  select cast(s.nextval as varchar2(10)) into :new.i from dual;
  4  end;
  5  /

Warning: Trigger created with compilation errors.

SQL> sho err
Errors for TRIGGER TRG:

LINE/COL ERROR
-------- -----------------------------------------------------------------
2/34     PLS-00103: Encountered the symbol "(" when expecting one of the
         following:
         . ) @ %

SQL> select  cast(s.nextval as varchar2(10)) from dual;

CAST(S.NEX
----------
1

Why is the CAST function not working in the trigger?

Thanks 

Tom Kyte
August 09, 2005 - 11:18 am UTC

it is not needed at all. if :new.i is a varchar2(10), it'll happen.

looks like 8i didn't like that but current software is OK with it.

"Before" Trigger Behaviour

maverick, December 13, 2005 - 3:20 pm UTC

Tom, question on Before insert or update or delete trigger.

I am trying to write a trigger on "dept" table,whenever there is a successful DML on dept,
it has to write a record in Log table.

Problem i am facing is, trigger is getting fired even if that DML is a failure.

eg:- I am trying to insert a varchar value into a deptno [number], just to see if trigger fires ,
and it does, even after going to when others section in exception

Why is that so and can I force trigger to fire only on success?

CREATE TABLE DEPT
(
DEPTNO NUMBER(5),
DNAME VARCHAR2(20 BYTE),
LOC VARCHAR2(20 BYTE)
)

CREATE TABLE LOG_TABLE
(
MESSAGE VARCHAR2(2000 BYTE),
TIME_STMP DATE DEFAULT sysdate
)

--Trigger on dept table:

CREATE OR REPLACE TRIGGER DEPT_TRG
BEFORE DELETE OR INSERT OR UPDATE
ON DEPT
FOR EACH ROW
begin
insert into log_table(message) values('DML on DEPT Table ');
end;
/

--function for DML on dept
create or replace function dml_dept
return integer
is
begin

insert into dept(deptno) values('testing trigger behaviour');
exception
when others then
return -1;
end;

Tom Kyte
December 13, 2005 - 5:26 pm UTC

use AFTER trigger. wait until the dml has actually been processed.

but don't forget, the statement can still rollback later - eg: it can still "fail" later on.

But why is trigger getting executed?

A reader, December 14, 2005 - 3:10 pm UTC

Doesn't Oracle know it's gone to exception and should not fire any triggers ?

Can you please explain your reply:
<<Quote>>
but don't forget, the statement can still rollback later - eg: it can still "fail" later on.
<</Quote>>

Thanks,

Tom Kyte
December 14, 2005 - 3:51 pm UTC

the "exception" is not happening until it happens - the BEFORE trigger you have fires well before the first byte of data is touched.


update t set x = 5;
rollback;


your trigger can still fire, but never have fired against any data that actually gets changed.

Got it. Thanks.

A reader, December 14, 2005 - 4:25 pm UTC

Tom, if i want to use only Before trigger [to capture :old values], and do not want trigger to fire in case of exception, Is there any way to do it?

Thanks,

Tom Kyte
December 14, 2005 - 5:45 pm UTC

no, the trigger fires BEFORE any data is touched.

use an AFTER trigger.

What should be the fireing sequence of after row triger and foreign key constraint

Mihail Daskalov, September 05, 2006 - 12:14 pm UTC

I thought
Here is some sample which shows that after row statement fires event though the integrity constraint check should have failed first.
Me and my colleagues are confused with the definition found at
</code> http://download-uk.oracle.com/docs/cd/B19306_01/server.102/b14220/triggers.htm#sthref3288 <code>

drop table emp;
drop table dept;

-- Create table
create table DEPT
(
ID NUMBER not null,
name VARCHAR2(100)
);

alter table DEPT add primary key (ID);
;


create table EMP
(
ID NUMBER not null,
NAME VARCHAR2(100),
DEPT_ID NUMBER
);

alter table EMP
add primary key (ID);

alter table EMP
add constraint EMP_DEPT_FK foreign key (DEPT_ID)
references DEPT (ID);

create or replace trigger EMP_AFT_INS_ROW_TRG after insert on emp for each row
declare
n number;
begin
select 1 into n from dept where dept.id = :new.dept_id;
exception
when no_data_found then
raise_application_error(-20001, 'No such department');
end;
/

insert into dept(id, name) values (1,'First department');
commit;
insert into emp (id, name, dept_id) values (101, 'First employee', 1);
commit;

rem here I expect a foreign key violation message instead of 'No such department' application error
insert into emp (id, name, dept_id) values (102, 'Second employee', 2);
commit;






Tom Kyte
September 05, 2006 - 5:19 pm UTC

constraints technically are validated AFTER the statement executes - it is not really defined whether a trigger will or will not fire (and if you have logic that depends on such intricate ordering, we have a really big logic problem)

but a for each row trigger - sure, because tables are allowed to violate their constraints during the DML, think about this:

update emp set empno = empno+1;

every row updated could temporarily violate uniqueness as there could be two empno =2, empno=3 and so on as this goes through row by row.

a research topic pops up

Alberto Dell'Era, September 06, 2006 - 2:57 pm UTC

> because tables are allowed to violate their
> constraints during the DML, think about this:
>
> update emp set empno = empno+1;

That means that the server process has to maintain a list of the modified blocks, to validate them when the stmt ends [and for deferred constraints - when the tx ends]. Correct?

If yes, keeping the list in memory doesn't look very scalable, so ... will the process revisit the rollback segment perhaps ?

TIA (as always)

Tom Kyte
September 06, 2006 - 4:07 pm UTC

</code> http://esemrick.blogspot.com/

see the link "oracle redo generation", that'll give you some insight 
http://72.32.8.36/custommusic/esemrick.pdf <code>

Update primary_key=primary_key+1

A reader, September 07, 2006 - 12:52 am UTC

Here is the exact link to the pk=pk+1 topic.

</code> http://esemrick.blogspot.com/2006/02/ora-000015-unique-constraint-not.html <code>

Trigger

R.Krishna Kumar, September 07, 2006 - 3:20 am UTC

Hi Tom
Kindly help me out

1) Trigger T1 is created by user U1 against a table T1 and also the user U1 has granted full permission to the user U2. Now, when the user U2 try to insert/update/delete from table T1, Should Trigger fire or not?

2)vise versa of the above, User U2 (has all permission for the table t1 belongs to user U1) has created trigger T2.
Now,When user u1 try to insert/update/delete from table T1, Should Trigger fire or not?

Regards
R.Krishna Kumar

Tom Kyte
September 07, 2006 - 7:19 am UTC

1) triggers fire, yes. you do not grant execute on them to others.

2) see number 1.

triggers are like "indexes", they are just used.

Trigger

R.Krishna Kumar, September 07, 2006 - 3:20 am UTC

Hi Tom
Kindly help me out

1) Trigger T1 is created by user U1 against a table T1 and also the user U1 has granted full permission to the user U2. Now, when the user U2 try to insert/update/delete from table T1, Should Trigger fire or not?

2)vise versa of the above, User U2 (has all permission for the table t1 belongs to user U1) has created trigger T2.
Now,When user u1 try to insert/update/delete from table T1, Should Trigger fire or not?

Regards
R.Krishna Kumar

to R.Krishna Kumar

Michel Cadot, September 07, 2006 - 7:50 am UTC

Of course triggers will fire. They are associated to the table not to the user:

SQL> create user u1 identified by u1;

User created.

SQL> grant create session, create table, create trigger to u1;

Grant succeeded.

SQL>  alter user u1 quota unlimited on ts_d01;

User altered.

SQL> create user u2 identified by u2;

User created.

SQL> grant create session, create any trigger to u2;

Grant succeeded.

SQL> connect u1/u1
Connected.
U1> create table t1 (col number);

Table created.

U1> create trigger trg1 before insert on t1 
  2  begin
  3    dbms_output.put_line('TRG1');
  4  end;
  5  /

Trigger created.

U1> grant all on t1 to u2;

Grant succeeded.

SQL> connect u2/u2
Connected.
U2> create trigger trg2 before insert on u1.t1
  2  begin
  3    dbms_output.put_line('TRG2');
  4  end;
  5  /

Trigger created.

U2> insert into u1.t1 values (0);
TRG2
TRG1

1 row created.

U2> connect u1/u1
Connected.
U1> insert into t1 values (1);
TRG2
TRG1

1 row created.

Now some issues for you:
1/ To be able to create a trigger in another schema you gave to U2 the CREATE ANY TRIGGER privilege which a VERY BIG security hole
2/ When you export U2 schema, what happens to U2.TRG2?
3/ When you export U1 schema, what happens to U2.TRG2?
4/ When you reimport the previous, what happens to your application logic?

Regards
Michel
 

Mihail Daskalov, September 07, 2006 - 8:04 am UTC

<quote>
constraints technically are validated AFTER the statement executes - it is not
really defined whether a trigger will or will not fire (and if you have logic
that depends on such intricate ordering, we have a really big logic problem)
</quote>

So the documentation which states that for each row first
before row triggers fire, then constraints are checked and the after row triggers fire is incorrect? Is this the conclustion?

</code> http://download-uk.oracle.com/docs/cd/B19306_01/server.102/b14220/triggers.htm#sthref3289 <code>

Thanks,
Mihail

Tom Kyte
September 07, 2006 - 8:25 am UTC

not really, it is just that "it is more complex than can be simply stated as a sequence of operations"

a simple check constraint - sure

but a constraint that needs to look "across rows" like a primary key or foreign key - they are conceptually done AFTERWARDS.

let me just say:

if you are dependent on the ordering of firing of same type triggers, whether a constraint is fired before or after a specific trigger - we have a problem, there is something wrong with the logic there.

Alberto Dell'Era, September 10, 2006 - 5:03 pm UTC

Self-correction:

>> because tables are allowed to violate their
>> constraints during the DML, think about this:
>>
>> update emp set empno = empno+1;
>
> That means that the server process has to maintain a list of the modified
> blocks, to validate them when the stmt ends [and for deferred constraints -
> when the tx ends]. Correct?

I no longer think that a list of modified blocks is necessary; all it takes it's just a counter of violations (index collisions) maintained as the DML executes.

Thanks to Tom for the two links to the Eric S. Emrick blog (very interesting) and thank to "A Reader" for the "exact link to the pk=pk+1 topic", also to Eric's blog :)

A viewer

KrishnaKanth, September 19, 2006 - 11:16 am UTC

Tom,
it's very helpful for me. But I have one more related Question on triggers. I am updating one table with after I/U/D for each row... But that is being done from the front end. So I need to track which columns are being updated. As I am fetching the columns from USER_TAB_COLUMNS. I am able to get the columns with:
declare
cursor c_cur is select column_name from user_tab_columns where table_name = 'EMP_MAS';
begin
for i in c_cur loop
variable := i.column_name;
end;
--- But here I want the column value also...?
How can I get those values ..
if I use
variable := ':new.'||i.column_name;
means I am getting ':NEW.COL_NAME' not the value.
and I tried with
execute immediate ('select :new.'||i.column_name||' from dual) into variable2;
But it is giving error like 'ORA-01008 not all variables bound'.



u r king

A reader, November 25, 2006 - 10:41 am UTC

Hi,

Too good.



DB_LINK in a row level trigger.

Amit, March 30, 2007 - 5:35 am UTC

Hi Tom,
I need your suggestion in following scenario.
There are two databases( Say 'A' and 'B'). In database 'A' There is a table 'T'. I'm having a trigger on T which is firing on UPDATE+INSERT+DELETE events. This trigger is inserting the records in a table 'T2' in database 'A' as well as table 'T4' & 'T5' . These 'T4' & 'T5' tables are in database 'B' therefore a db_link is used in for inserting the data in T4 & T5 tables.
As a DB_LINK is used, it is delaying the process. My manager wants me to improove the process. I suggested him to go MATERIALIZED VIEW. but according to him it is not a realtime solution for this process. Is there any means by which we can improove the performance. ( Even I used AUTONOMOUS TRANSAXTION in this trigger but performance has not improoved as desired)

Tom Kyte
March 30, 2007 - 1:33 pm UTC

I can improve this process.

lose database B, drop table t2, t4 and t5 - consolidate this all.


if this data needs to be syncronously maintained in two databases - you need a single database - period, having two is not sensible.


unless you can go asynchronous (no two phase commit), slow=true will be "fact"

and complex check?

knopfler, October 04, 2007 - 6:05 am UTC

[quote]not really, it is just that "it is more complex than can be simply stated as a sequence of
operations"

a simple check constraint - sure

but a constraint that needs to look "across rows" like a primary key or foreign key - they are
conceptually done AFTERWARDS.

let me just say:

if you are dependent on the ordering of firing of same type triggers, whether a constraint is fired
before or after a specific trigger - we have a problem, there is something wrong with the logic
there. [/quote]

Hi Tom. First of all, excuse me if I don't write correctly in english language, but I'm from Spain and my English is far from being perfect.

a) So, you are saying that doc is wrong. I'm disappointed with this. It was very easy to write just your paragraph ("but a constraint that needs to look "across rows" [...]) in the documentation.

b) You say "there is something wrong with the logic
there". But how can I add a complex check involving cross-table checking statements if there is a foreing key between those two tables?
I mean: I have table A (with column "type"), and tables B, C and D with foreign key to A table. A row in A table can be referenced by multiple rows in B, C or D tables. But I want to check that all values in B, C and D table are referencing values in A table that fulfills a certain condition (in B table, that corresponding rows in A have type = 1, in C table, that corresponding rows in A have type = 2, and so on...).
Do you mean that there is a model design fault in that scenario if I have that requeriments?

Thank you very much for your help.

Before Update Trigger behavior when updating multiple records

Raghav, May 09, 2008 - 10:36 am UTC

Hi Tim

Suppose, I have a table having country currency combinations with a primary currency.
The below is a sample data.

Country Currency Primary_Currency
US USD Y
US USN N
US USS N

And, I have a before update trigger on the above table for each row (using autonomous transaction to avoid mutating error) to check whether the country has any primary currency.

I am giving the below update.

Update Country_Currency_Ref
set is_primary_currency = 'Y'
where (country_code = 'US' and currency_code = 'USN');

And the trigger is working fine and giving the correct message that it cannot be updated as there is already one primary currency against that country

Now I update the data as below (No primary currency)

Country Currency Primary_Currency
US USD N
US USN N
US USS N

and try to update the data with the below condition

Update Country_Currency_Ref
set is_primary_currency = 'Y'
where (country_code = 'US' and currency_code <> 'USD');

In this case, the trigger is failing and updating other two records.

Why is this behaviour? And how can we over come the same?

Thanks and Regards
Raghav
Tom Kyte
May 12, 2008 - 10:38 am UTC

... And, I have a before update trigger on the above table for each row (using
autonomous transaction to avoid mutating error) to check whether the country
has any primary currency.
....

I have a different way to describe that, I can shorten that sentence to be simply:

I have a huge multi-user concurrency bug in my program


ugh - this is precisely why I hate, despise and would like to remove:

a) triggers
b) autonomous transactions


they are so universally MISAPPLIED.


Do you understand that two users, running your code at the same time, would FAIL in a miserable way????


drop your trigger.

and promise to never use autonomous transactions to avoid mutating table constraints (I said "mutating table CONSTRAINTS", not error - the error is you using an autonomous transaction to destroy your data integrity) ever again.


add:

create unique index only_one_can_be_primary on country_currency_ref
( case when is_primary_currency = 'Y' then country_code end );



it should be obvious why the trigger is failing, you used an autonomous transaction, it is as if you are querying the table from ANOTHER SESSION entirely - you cannot see your modifications.

Just like would happen if two users did:

insert into that_table ( 'US', 'Y' );

at the same time, they would both insert the primary currency!!!?!?!?!?



I hate triggers
I hate autonomous transactions
I hate when others

if we removed those three things from PLSQL - we would solve 90% of all application bugs I think... No kidding.

trigger behaviour when updating multiple records

Raghav, May 13, 2008 - 6:22 am UTC

Hi Tom,

Thank you very much for your feedback and valuable suggestion. It worked like a miracle. Till now, we dont know that an index can be created depending on some conditions like case structure you have shown. It is really a great thing that can be used avoiding triggers. Thank you once again for your timely help.

Thanks and Regards
Raghav

Constraints to enforce business rules

Joe C., May 14, 2008 - 4:23 pm UTC

I also didn't realize either that you could use a function based index like to enforce a business rule. I always thought that was where nasty triggers stepped in.

Fantastic. Now I am going to spam all our other dba's to make sure they understand as well.

Joe

time minus time into one column

Abid Ali, May 22, 2008 - 1:49 am UTC

I have daily_log table and structure is as under.
Name Null? Type
------------------------------- -------- ----
CO_CODE VARCHAR2(2)
CARD_NO VARCHAR2(6)
INOUT VARCHAR2(3)
SYS_DATE DATE
SYS_TIME DATE

and my data are
CARD_no INOu SYS_DATE TIME
------ --- --------- -----
000378 OUT 10-MAY-08 08:40
000378 IN 10-MAY-08 08:41
000501 OUT 12-MAY-08 14:30
000501 IN 12-MAY-08 16:30
000333 OUT 14-MAY-08 17:19
000333 IN 14-MAY-08 17:35
000607 OUT 14-MAY-08 10:01
000607 IN 14-MAY-08 10:35
000542 OUT 13-MAY-08 10:44
000542 IN 13-MAY-08 10:50

My result should be as under.

CARD_no INOu SYS_DATE TIME
------ --- --------- -----
000378 OUT 10-MAY-08 08:40
000378 IN 10-MAY-08 08:41 ----> 00.01
000501 OUT 12-MAY-08 14:30
000501 IN 12-MAY-08 16:30 ----> 02.00
000333 OUT 14-MAY-08 17:19
000333 IN 14-MAY-08 17:35 ----> 00.16
000607 OUT 14-MAY-08 10:01
000607 IN 14-MAY-08 10:35 ----> 00.34
000542 OUT 13-MAY-08 10:44
000542 IN 13-MAY-08 10:50 ----> 00.06

please send me in detail about to solve my problem.
i shall be highly appreciated you.

Thanks

Abid
Tom Kyte
May 22, 2008 - 7:15 am UTC

no create table
no insert into
no LOOK

and your sample output/input is rather meaningless there

you have two dates in your table

you show output as one date being just a "day" and the other date just being a "time" - that makes no sense to me whatsoever. You do not store "days" in one column and "time" in another.


This looks like a simple lag() function call - read about lag()

hi tom

harry, June 27, 2008 - 12:23 pm UTC

hi tom,

what is kyd factor & where did it use?
Tom Kyte
June 27, 2008 - 12:32 pm UTC

that is a very good question.

It could be the Cayman Islands conversion factor (their currency is KYD)



Indeed, what is the "kyd factor"

I've not heard of it, do you have perhaps a little bit of "context" to go with that.

KYD Factor

Greg, June 27, 2008 - 3:23 pm UTC

How about "Know Your Data"? Sounds like someone that runs this site might say... :-)

Avoiding triggers

Raghav, August 12, 2008 - 3:40 am UTC

Hi Tom

Here is another question in avoiding triggers. If suppose, we want to enforce data integrity in a column which refers to the same table, is that possible to have a conditional index (function based index) to achieve the result? Here is my example.

agency
code code equivalent_orr
ORR 1
ORR 2
ORR 3
ORR 4
ORR 5
CBS 2+ 3
CBS 2- 1
CBS 3+ 4
CBS 3- 1
CBS 4+ 5

I need to map equivalent values of ORR to CBS rows.

I have tried using exists clause as I need to check from the list of values of ORR as below.

CREATE INDEX REF1
ON CODE_REFERENCE
(Case When EXISTS (SELECT CODE FROM CODE_REFERENCE
WHERE AGENCY_CODE = 'ORR')
Then EQUIVALENT_ORR End
)

The index is created. But when I try to access the table, the session is getting disconnected. How can we achieve this situation without using triggers. Please help me in this regard.

Thanks & regards
Raghav
Tom Kyte
August 13, 2008 - 4:07 am UTC

I have no idea what you are trying to do.

"I need to map equivalent values of ORR to CBS rows. "

is not very clear, providing a snippet of code that does not work - that doesn't show us what you mean either - I read your create index and cannot figure out what you were trying to do.

Please post your issue in "specification form", like you would in a formal design document - so that another developer would be able to develop code from your specification.

Your function seems to say "as long as there is a row in the table with agency_code = ORR, then index this column - else do not", literally, that is what it says - but that doesn't seem to make sense (nor will it work, that would mean a delete against this table would have the ability to effectively empty out the entire index and an insert could cause the entire index to be constructed in effect)

Avoiding triggers

Raghav, August 13, 2008 - 7:17 am UTC

Hi Tom,

Thanks for your reply. I was trying (may be wrong trying same medicine for different deceases) to avoid trigger / autonomous transaction in similar manner as to my previous posting of country currency ref.

Our requirement is to enforce a conditional RI referring to the same table. What ever the value being updated in equivalent_orr has to exist in the same table where the agency_code is ORR. In this case we are accessing the same table for query and update so there is a chance of mutating error.

How can we achieve this without using triggers? Your help is well appreciated.

Thanks and Regards
Raghav

Tom Kyte
August 18, 2008 - 9:07 am UTC

yes, you correctly model the data.

there is no such thing as "conditional RI", you either

a) have it
b) or not

you would use a column, this column is nullable, this column will be populated with the correct value, this column will have RI back to the same table.

You cannot use a trigger to do RI unless your trigger has LOCK TABLE in it, remember that - you will HAVE TO LOCK THE ENTIRE TABLE if you even attempt this on your own.

Order of triggers

Bill B., August 14, 2008 - 4:11 pm UTC

Just an FYI,
In oracle 11G the create trigger command may now include a FOLLOWS option to force the order of the triggers firing at the same timing point (BEFORE STATEMENT, BEFORE ROW, AFTER ROW, AFTER STATEMENT);
Tom Kyte
August 18, 2008 - 10:12 am UTC

I would still encourage:

a) DO NOT USE TRIGGERS, STAY AWAY FROM THEM

b) if you do not do A, then your trigger should be all of one line long:


begin
package.procedure( :new.a, ..... );
end;

and you have only one of each type.

Avoiding triggers

Raghav, August 19, 2008 - 2:30 am UTC

Hi Tom,

Thanks for your reply once again. I understand from your reply that we can still enforce RI on the table without using triggers. I still need a clarification - as the table is having a primary key on agency code and code (combinational primary key), can we enforce RI on a part of primary key i.e, code? Can you please show us how can we achieve the result in the above example?

Thanks and Regards
Raghav
Tom Kyte
August 20, 2008 - 10:22 am UTC

... can we enforce RI on a part of primary key ...

I don't really know what that means

you need:


a) a unique constraint or primary key on a set of columns
b) then you can refer to them


so, do you have a unique set of columns
and another set of columns (having the same number of columns) that needs to refer to them

if so - referential integrity is there

if not - your data model is not yet complete, you are missing an entity, after you add it you'll have referential integrity

Avoiding Triggers

Raghav, August 20, 2008 - 11:11 am UTC

Hi Tom

As you said it correctly, if we have two sets of columns - one set for primary key and another set for reference we can enforce RI. But, we have a set of columns agency code and code (2 columns) as primary key, and only one column 'ORR_Equivalent' in which we want to accept the values of code where the agency code is 'ORR'.

2. Suppose if we have another column 'ORR' the values in that column will 'ORR' throughout the table which is redundant, as we know we are going to map only ORR equivalent values in the third column.

Also, you have mentioned that there is no concept of conditional RI. So, in such scenerio how can we get the result by avoiding triggers.

Thanks and Regards
Raghav
Tom Kyte
August 20, 2008 - 12:42 pm UTC

... and only one column
'ORR_Equivalent' in which we want to accept the values of code where the agency
code is 'ORR'.
...

alter table t add constraint x
check (orr_equivalent is null OR agency_code = 'ORR');


make sure orr equivalent is null OR the agency code is set to the value, only then would you be able to put a value in there.

#2 made no sense to me as I read it.

... the values in that column will 'ORR'
throughout the table which is redundant ...

lost me right there.


you will almost certainly have to model this "differently" in order to have clean data.

Avoiding Triggers

Raghav, August 22, 2008 - 9:53 am UTC

Tom

Sorry for the confusion happened through my previous posting. I am re pharasing my query. As per the below example we want either NULL or the values (code) equal to ORR (agency_code) in the equivalent_orr.

Primary key(2 columns) new column
----------- --------- -----------------
agency code code equivalent_orr
----------- --------- -----------------
ORR 1
ORR 2
ORR 3
ORR 4
ORR 5
CBS 1 3
CBS 2 1
CBS 3 4
CBS 4 1
CBS 5 5

As you mentioned -
"there is no such thing as "conditional RI", you either
a) have it
b) or not "

In this scenerio, other than the model changes (i.e., adding one more column to make it a set of two columns to refer to foreign key of same table) is there any possibility of obtaining the result without using triggers.

Thanks and Regards
Raghav

Tom Kyte
August 22, 2008 - 2:54 pm UTC

you need a table where ORR is a unique/primary key value - where is that table, you just define referential integrity to it.

You are missing a table. The table with the unique ORR codes in it.

Avoiding Triggers

Raghav, August 25, 2008 - 6:13 am UTC

Hi Tom,

Thank you once again for your reply. I understood from your response that, in the given scenario, without model changes / without a trigger (may be a clumsy process) it is not possible to enforce RI.

I have another scenario, where again I want to avoid the use of a trigger. There is a Transaction table in which one of the column values are to be part of any one of the ten other tables. I took some (similar) raw example below.

Table A
---------
apples
mangoes
oranges

Table B
---------
Cars
Buses
Trucks

Table C
---------
Houses
Signle Storied Buildings
Multi Storied Buildings

and so on (many different tables).

Table T
-----------
Sno Reference table Value
---- --------------- -------
1 A mangoes
2 C Houses
3 B Trucks
---- --------------- --------
---- --------------- --------
---- --------------- --------


Other than the supposed model like below (which is not possible at this moment) having two columns and having an RI on two columns

Table category
------------------
Fruits mangoes
Fruits apples
Fruits oranges
Vehicles Cars
Vehicles Buses
---------- --------
---------- --------
---------- --------

Is there any way to achieve the result, i.e, enforcing an RI? Please let me know the best possible way for the same.

Thanks and Regards
Raghav

Tom Kyte
August 26, 2008 - 8:34 pm UTC

unless you use LOCK TABLE, you cannot do RI in a trigger - please remember that, it is not clumsy, it is SERIAL.

... There is a Transaction table in which one of the column values are to be part of any one of the ten other tables ...

we need to have a long long talk with your "data modeler", because - you know what - they are not, they are not a data modeler.

sorry, there will quite simply be no simple answer here, this is yet another "model botched beyond repair"

Constraints versus Triggers

A Reader, March 04, 2009 - 3:59 pm UTC

Tom

Which is faster for record inserts?

1. NOT NULL constraints with DEFFAULT values.

AUDIT_CREATED_ID VARCHAR2(30) DEFAULT USER NOT NULL,
AUDIT_CREATED_DATE DATE DEFAULT SYSDATE NOT NULL,
AUDIT_LAST_UPDATED_ID VARCHAR2(30) DEFAULT USER NOT NULL,
AUDIT_LAST_UPDATED_DATE DATE DEFAULT SYSDATE NOT NULL


or

2. use Triggers to populate the default values for columns and the columns are defined as follows, without NOT NULL constraints)

AUDIT_CREATED_ID VARCHAR2(30),
AUDIT_CREATED_DATE DATE,
AUDIT_LAST_UPDATED_ID VARCHAR2(30),
AUDIT_LAST_UPDATED_DATE DATE,

or

3. use Triggers to populate default values for columns and the columns are defined as follows, with NOT NULL constraints)

AUDIT_CREATED_ID VARCHAR2(30) NOT NULL,
AUDIT_CREATED_DATE DATE NOT NULL,
AUDIT_LAST_UPDATED_ID VARCHAR2(30) NOT NULL,
AUDIT_LAST_UPDATED_DATE DATE NOT NULL,


Thanks
Tom Kyte
March 04, 2009 - 4:21 pm UTC

avoid triggers like the plague, just say no to triggers.

If you can do it in a declarative fashion- DO IT that way.


just say NO to triggers, period.
https://www.oracle.com/technetwork/issue-archive/2008/08-sep/o58asktom-101055.html


you will find them to be infinitely less performant than integrity constraints

you will find them a nuisance, they are "magic", they are "side effects", they will bite you back in the future.


Conditional Index is not getting picked up in select queries.

Bhavya, January 05, 2011 - 7:06 am UTC

Dear TOM,
Requirement is to create a unique index on the 5 columns( name columns as shown in the index creation below) when ever the data_set_type='UNQ'. I have used the following conditional index to achieve the same. How ever, the select queries written go for full table scan. Is there a way to change the queries for unique scan of the index / or a better way to create the conditional unique index. Your help on this is highly appreciated.

SQL> select * from v$version ;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
CORE 10.2.0.4.0 Production
TNS for Linux: Version 10.2.0.4.0 - Production
NLSRTL Version 10.2.0.4.0 - Production

SQL> CREATE TABLE PERSON_PROFILE
  2  (
  3    PERSON_ENTITY_ID    NUMBER NOT NULL,
  4    NAME_PREFIX         VARCHAR2(96 CHAR),
  5    FIRST_NAME          VARCHAR2(384 CHAR),
  6    MIDDLE_NAME         VARCHAR2(384 CHAR),
  7    LAST_NAME           VARCHAR2(384 CHAR),
  8    NAME_SUFFIX         VARCHAR2(96 CHAR),
  9    DATA_SET_TYPE  VARCHAR2(32 CHAR)
 10  )
 11  /

Table created.

SQL> create unique index idx_pp_uk on person_profile
  2  (CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN "FIRST_NAME" ELSE NULL END ,
  3  CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN "MIDDLE_NAME" ELSE NULL END ,
  4  CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN "LAST_NAME" ELSE NULL END ,
  5  CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN "NAME_PREFIX" ELSE NULL END ,
  6  CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN "NAME_SUFFIX" ELSE NULL END )
  7  /

Index created.

SQL> begin
  2  for i in 1..3000
  3  loop
  4  insert into person_profile values ( i, 'NP', 'FN', 'MN', 'LN', 'NS', 'NUNQ') ;end loop;
  5  commit;
  6  for i in 3001..6000
  7  loop
  8  insert into person_profile values ( i, 'NP'||i, 'FN', 'MN', 'LN', 'NS', 'UNQ') ;
  9  end loop;
 10  commit;
 11  end ;
 12  /

PL/SQL procedure successfully completed.

SQL> analyze index idx_pp_uk validate structure;

Index analyzed.

SQL> 
SQL> select name, lf_rows from index_stats;

NAME                              LF_ROWS
------------------------------ ----------
IDX_PP_UK                            3000                                                        

SQL> 
SQL> analyze index idx_pp_uk compute statistics;

Index analyzed.

SQL> alter session set query_rewrite_enabled = true;

Session altered.

SQL> 
SQL> alter session set query_rewrite_integrity = trusted;

Session altered.

SQL> explain plan for
  2  select *  from person_profile a
  3  where
  4  "FIRST_NAME"    = CASE WHEN "DATA_SET_TYPE" = 'UNQ' THEN :first_name     ELSE NULL END and
  5  "MIDDLE_NAME"   = CASE WHEN "DATA_SET_TYPE" =  'UNQ' THEN :middle_name    ELSE NULL END and
  6  "LAST_NAME"     = CASE WHEN "DATA_SET_TYPE" =  'UNQ' THEN :last_name      ELSE NULL END and
  7  "NAME_PREFIX"   = CASE WHEN "DATA_SET_TYPE" =  'UNQ' THEN :name_prefix    ELSE NULL END and
  8  "NAME_SUFFIX"   = CASE WHEN "DATA_SET_TYPE" =  'UNQ' THEN :name_suffix    ELSE NULL END
  9  
SQL> 
SQL>  SELECT * FROM table(DBMS_XPLAN.DISPLAY); 

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 4257669096

------------------------------------------------------------------------------------
| Id  | Operation         | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |                |     1 |  2089 |     8   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| PERSON_PROFILE |     1 |  2089 |     8   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------

   1 - filter("FIRST_NAME"=CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN :FIRST_NAME
              ELSE NULL END  AND "MIDDLE_NAME"=CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN
              :MIDDLE_NAME ELSE NULL END  AND "LAST_NAME"=CASE "DATA_SET_TYPE" WHEN 'UNQ'
              THEN :LAST_NAME ELSE NULL END  AND "NAME_PREFIX"=CASE "DATA_SET_TYPE" WHEN
              'UNQ' THEN :NAME_PREFIX ELSE NULL END  AND "NAME_SUFFIX"=CASE
              "DATA_SET_TYPE" WHEN 'UNQ' THEN :NAME_SUFFIX ELSE NULL END )

Note
-----
   - dynamic sampling used for this statement

22 rows selected.


Tom Kyte
January 05, 2011 - 7:44 am UTC

you used two different functions - one in the index and another in the query.


CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN "FIRST_NAME" ELSE NULL END

is not the same as:

CASE WHEN "DATA_SET_TYPE" = 'UNQ' THEN :first_name ELSE NULL END


First - one of them uses "case X when value then" and the other uses "case WHEN x=value then"

Second, you are using a bind variable in the function, you didn't (you cannot) index a bind variable.


I think you MEANT to code:


where
CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN "FIRST_NAME" ELSE NULL END = :first_name
.....




Thanks

Bhavya, January 05, 2011 - 10:36 am UTC

Thanks a lot Tom. This worked. Really appreciate your immediate response.

SQL>  explain plan for
  2  select *  from person_profile a
  3  where
  4  CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN FIRST_NAME ELSE NULL END = :first_name and
  5  CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN Middle_NAME ELSE NULL END = :middle_name and
  6  CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN LAST_NAME ELSE NULL END = :last_name and
  7  CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN NAME_prefix ELSE NULL END = :name_prefix and
  8* CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN NAME_SUFFIX ELSE NULL END = :name_suffix
SQL> /

Explained.

SQL> 
SQL> 
SQL> SELECT * FROM table(DBMS_XPLAN.DISPLAY); 

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------
Plan hash value: 1146264087

----------------------------------------------------------------------------------------------
| Id  | Operation                   | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                |     1 |  2089 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| PERSON_PROFILE |     1 |  2089 |     2   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | IDX_PP_UK      |     1 |       |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------
---------------------------------------------------

   2 - access(CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN "FIRST_NAME" ELSE NULL END
              =:FIRST_NAME AND CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN "MIDDLE_NAME" ELSE NULL END
              =:MIDDLE_NAME AND CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN "LAST_NAME" ELSE NULL END
              =:LAST_NAME AND CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN "NAME_PREFIX" ELSE NULL END
              =:NAME_PREFIX AND CASE "DATA_SET_TYPE" WHEN 'UNQ' THEN "NAME_SUFFIX" ELSE NULL END
              =:NAME_SUFFIX)

Note
-----

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------
   - dynamic sampling used for this statement

23 rows selected.

Trigger Help

Mitch, April 05, 2011 - 3:02 pm UTC

Here is my trigger

create or replace trigger orderscomplete
before update of order_completed on orderheader
for each row
when(orderline.orderline_fufilled = 'Y')
begin
:new.order_completed :(SYSDATE);
end;
/

can you please advise I am using two tables 'Orderheader' and 'Orderline' when the Orderline_fufilled = 'Y' it will update the order_completed in orderheader to systdate
Tom Kyte
April 12, 2011 - 12:45 pm UTC

I don't see two tables, in fact I don't see any tables at all?


Are you saying in effect:

every time the orderline table is updated and the column orderline_fulfilled is set to 'Y' (changed to 'Y' I presume??? Not just having a value of 'Y' as it could be some other column that was modified????) then update a different table orderheader and set the order_completed column to sysdate?


If so, this is a fairly bad design don't you think?

The order isn't completed, the line item was - the order may or may not be.

The model should be:


create table order ( order_id, ..... );

create table line_items
( order_id references order,
line_item,
orderline_fulfilled DATE,
...
primary key(order_id,line_item)
);


and instead of setting ANYTHING to 'Y', you set orderline_fulfilled = SYSDATE.

And then you can query

select order_id, max(orderline_fulfilled) from line_items where order_id = ?;


to get the information you thought you wanted to keep.


Meaning, you don't have any Y/N column, you have a date column that is either NULL (date of fulfillment is unknown) or populated (the date it was fulfilled). And if you want to know the latest date for an order, you just use max.

trigger

A reader, December 22, 2011 - 7:25 pm UTC

Excellent!

Need your view

Biswaranjan, June 18, 2012 - 4:28 am UTC

Hi Tom,

##############################
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE 10.2.0.1.0 Production
TNS for 32-bit Windows: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production

###########################################
I have done exactlly what "maverick from US dec-14th-2005" on this page done and did not get anything written into
the log_table (which happened as I expected).




CREATE TABLE DEPT
(
DEPTNO NUMBER(5),
DNAME VARCHAR2(20 BYTE),
LOC VARCHAR2(20 BYTE)
) ;

CREATE TABLE LOG_TABLE
(
MESSAGE VARCHAR2(2000 BYTE),
TIME_STMP DATE DEFAULT sysdate
);

CREATE OR REPLACE TRIGGER DEPT_TRG
BEFORE DELETE OR INSERT OR UPDATE
ON DEPT
FOR EACH ROW
begin
insert into log_table(message) values('DML on DEPT Table ');
dbms_output.put_line('fired');
end;
/

create or replace function dml_dept
return integer
is
begin

insert into dept(deptno) values('testing trigger behaviour');
exception
when others then
return -1;
end;
/

declare
a number;
begin
a:=dml_dept;
end;
/


####### marveric written
I am trying to write a trigger on "dept" table,whenever there is a successful DML on dept,
it has to write a record in Log table.

Problem i am facing is, trigger is getting fired even if that DML is a failure.

eg:- I am trying to insert a varchar value into a deptno [number], just to see if trigger fires ,
and it does, even after going to when others section in exception

##################You replied
the "exception" is not happening until it happens - the BEFORE trigger you have fires well before
the first byte of data is touched.


################ My question is

I tested the same and found that the before row trigger is not at all fired.
But marveric found the trigger got fired ( may be some lower version fo oracle ,but now aware).

And I am confused , was it wrong what you stated or you stated as the version behaves at that
point of time.(need your view).

Thanks as always,
Biswaranjan.


Tom Kyte
June 18, 2012 - 9:08 am UTC

things change over time, you cannot count on a trigger firing only once, at least once and so on.

also, I never tested mavericks claims, I just looked at it and said "if you want to do something only after the row has been validated and inserted - use an after trigger"

I didn't state anything about his example other than "if you want to do something AFTER the row has been validated, do it AFTER the row has been validated"

continuation to my last post

Biswaranjan, June 18, 2012 - 9:17 am UTC

Thanks Tom for you reply and one thing I want to say
that you are too fast and too good( I was seeing your reply to all question for today one by one). People will not really believe that ask tom is a single person(Thomas kyte) but I can say "ask tom"="unbelievable" with out any doubt.
great.

thanks as always,
Biswaranjan.

continuation to my last post

Biswaranjan, June 18, 2012 - 9:31 am UTC

Hi Tom,

Just above you wrote
############
I didn't state anything about his example other than
"if you want to do something AFTER the row has been validated, do it AFTER the row has been validated"

#################

Just below to marveric one reader asked(dont know whether that is marveric or other)
##############
Doesn't Oracle know it's gone to exception and should not fire any triggers ?

Can you please explain your reply:
<<Quote>>
but don't forget, the statement can still rollback later - eg: it can still "fail" later on.
<</Quote>>

thanks,


Followup December 14, 2005 - 3pm Central time zone:

the "exception" is not happening until it happens - the BEFORE trigger you have fires well before
the first byte of data is touched.


update t set x = 5;
rollback;


your trigger can still fire, but never have fired against any data that actually gets changed.
#####################

I was just saying about the "the "exception" is not happening until it happens - the BEFORE trigger you have fires well before
the first byte of data is touched.".
Was it really true that time ?

I just want to clear my doubt abuot the line you wrote.

Excuse me if I said anything wrong to you.

thanks,
Biswaranja.
Tom Kyte
June 18, 2012 - 9:52 am UTC

Was it really true that time ?


it is still true today, that update is a multi-row operation - it isn't a single row thing, we might fire your before trigger millions of times and still ultimately roll back because the last row fails.

thanks

Biswaranjan, June 18, 2012 - 10:32 am UTC

Hi Tom,
:)

I was mixed up those two users question (marveric's and the reader's) so got confused with your follwup comments.
Now I understood.

By the way I was aware of this "we might fire your before trigger millions of times and still ultimately roll back because the last row fails."

All is your help :) .


Thanks as always,
Biswaranjan.

ksk, February 15, 2013 - 10:09 am UTC

Hi..TOM,

I need one query from you, we have one table(ex.KSK) and it had trigger applied for getting user's information like logon,logoff,and any changes in a table(ex.KSK) and etc. The trigger(ex.KSK_TRIG) is writes information when each user accessing table(KSK) everytime.. actually, this table and trigger created by DBA's.
So that my question is I want to see the details of latest or current record of each user's information except SYS and SYSMAN users ????
Tom Kyte
February 15, 2013 - 10:29 am UTC

so, add a where clause to your query against the table to retrieve records that are not created by sys or sysman?


I'm not sure what you are asking here.

How to get current records from a trigger table.

KSK, February 18, 2013 - 1:34 am UTC

Hi, Tom

One table(Example.abc) is existed in our database and it contains large records of information like username, hostname, logontime, logoff_time, action_name. Number of users accessing (abc) table when they can do any actions the trigger writes above information in that table. So my question is I need only current records of each users except sys. Please give me a query for getting current records of each users..
Tom Kyte
February 19, 2013 - 9:43 am UTC

hmmmm, i wonder what sort of things people might actually need to write a query, things like I don't know, maybe a create table, maybe some inserts, maybe some description of what constitutes "the current records" (sounds plural like you want more than one per user - and I don't know what field represents the most current. One approach would be to use login time, another equally valid would be to use logoff time, another is to use some other attribute or combination thereof that you haven't mentioned).....


do you think so too?


read:
http://www.oracle.com/technetwork/issue-archive/2007/07-jan/o17asktom-093877.html

especially the "Top- N Queries by SOMETHING" section. Once you've read and understood that - you'll be able to write this query (since you have the tables, the rows, and the detailed knowledge of what that "something" is :) )

Recent oracle versions allow specifying trigger order

Andrew Wolfe, March 30, 2017 - 5:32 pm UTC

There are now mechanisms to set the order of executing triggers.

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library