Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Koshal.

Asked: November 26, 2006 - 2:55 pm UTC

Last updated: November 14, 2012 - 8:31 pm UTC

Version: 9.2.0.7

Viewed 10K+ times! This question is

You Asked

Hi Tom

We got a de-normalized table with around 150 columns and around 50 millions rows. There are two columns which rarely gets updated. we want to track the changes. I defined the trigger in the following way.

CREATE OR REPLACE TRIGGER test
BEFORE UPDATE OR DELETE
ON test
FOR EACH ROW
WHEN ( NVL (new.user_id, -999999) <>
NVL (OLD.user_id,
-999999)
OR NVL (NEW.stts_id, -999999) <>
NVL (OLD.stts_id,
-999999)
)
Is there any other effective way. The problem with one I defined is for every update/delete (even that update does not involve these two columns) it is checking whether the column has changed or not. Dont you think we need such feature in Oracle, if not already available.

Thanks
Koshal


and Tom said...

you have a "when" condition

</code> http://docs.oracle.com/cd/B10501_01/server.920/a96540/statements_76a.htm#2063897 <code>

that would prevent the "body" from firing.


ops$tkyte%ORA9IR2> create table t ( x int, y int, z int );
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> create or replace trigger t_trigger
2 after update or delete on t
3 for each row
4 when (
5 (new.x <> old.x or (new.x is null and old.x is not null) or (new.x is not null and old.x is null))
6 OR
7 (new.y <> old.y or (new.y is null and old.y is not null) or (new.y is not null and old.y is null))
8 )
9 begin
10 dbms_output.put_line( 'I fired' );
11 end;
12 /
ops$tkyte%ORA9IR2> insert into t values ( 0, 0, 0 );
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> set feedback off
ops$tkyte%ORA9IR2> update t set x = 0;
ops$tkyte%ORA9IR2> update t set x = 1;
I fired
ops$tkyte%ORA9IR2> update t set x = null;
I fired
ops$tkyte%ORA9IR2> update t set x = 1;
I fired
ops$tkyte%ORA9IR2> update t set x = 1;
ops$tkyte%ORA9IR2>


Rating

  (13 ratings)

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

Comments

That is what I did.

Koshal, November 27, 2006 - 11:51 am UTC

In the question I asked, I used the when clause. I am just asking is there anything other way than when clause. (Because when clauses does the required and takes time)

for example
update emp set sal=sal+10
in this update statement there is no user/status column. So I dont want to do any checking.
update emp set sal=sal+10,staus=nvl(status,'COMPLETE');
In this case I do want to do the checking.
With when clause even though the body does not get fired, it takes timing for checking here are stats I found.
No of Records Without Trigger With Trigger Trigger with When Clause
323336 Run 1 16 Seconds 51 Seconds 28 seconds
323336 Run 2 16 Seconds 52 Seconds 28 seconds
323336 Run 3 20 Seconds 248 Seconds

Thanks
Koshal


Tom Kyte
November 27, 2006 - 7:36 pm UTC

the when clause is the way to have the trigger check to see if it should fire the body or not.

checking is "going to consume some time", absolutely.


(you sort of DO want to do checking, else it would - well - always fire)



What happens when you do this ?

A reader, November 27, 2006 - 11:20 pm UTC

What happens when you do --

before update of user_id or update of stts_id or delete on

Tom Kyte
November 28, 2006 - 7:13 am UTC

that would help "filter" it as well.

Checking at the statement level

koshal, November 28, 2006 - 9:25 am UTC

What I mean is we should have some sort of checking at the statement level itself. What is happening in the when clause is it is checking for each row.

It should be able to distinguish between

1. upate emp set sal=sal+10
and
2. update emp set status=nvl(status,'COMPLETE')

Looking at the the first statement we know that there is going to be no change to "status". I would be nice had oracle able to identify this at statement level and eliminate row level checking.

Thanks a lot tom.

Thanks
Koshal







Tom Kyte
November 28, 2006 - 9:41 am UTC

did you see the comment right above this?

trigger on columns

Matteo, November 28, 2006 - 11:46 am UTC

Hi,
you can create a trigger for actions to specific columns:
SQL> set serveroutput on size 10000
SQL> 
SQL> create table trigmat (nome varchar2(30), cognome varchar2(30))
  2  /

Table created

SQL> 
SQL> create or replace trigger trigmat_update_nome
  2  before update or delete of cognome on trigmat
  3  for each row
  4  
  5  begin
  6    dbms_output.put_line('cognome modified, old: '||:old.cognome||', new: '||:new.cognome);
  7  end;
  8  /

Trigger created

SQL> 
SQL> insert into  trigmat values ('john','smith');

1 row inserted

SQL> insert into  trigmat values ('albert','red');

1 row inserted

SQL> commit;

Commit complete

SQL> 
SQL> update trigmat set nome = upper(nome) where nome = 'john';

1 row updated

SQL> commit;

Commit complete

SQL> 

Trigger didn't fire....

SQL> update trigmat set cognome = upper(cognome) where cognome = 'smith';
cognome modified, old: smith, new: SMITH

1 row updated

SQL> commit;

Commit complete

SQL> 
 Now, on update of cognome field, trigegr fired...

SQL> delete trigmat where cognome ='red';
cognome modified, old: red, new: 

1 row deleted

SQL> commit;

Commit complete

SQL> 

Also on delete.

Regards
Matteo
 

context switch

A reader, November 28, 2006 - 11:50 am UTC

Tom,
one question on this. With any PLSQL code (which trigger is) there is a context switch - correct? Is it also true for WHEN clause in trigger? Is "WHEN" condition checked by PLSQL engine (not sure I'm correct with terminology) or Oracle is smart enough to do it in SQL context and in case condition fails escapes context switch?
thank you very much!

Tom Kyte
November 28, 2006 - 12:45 pm UTC

benchmark it.

Thanks a lot Matteo

koshal, November 29, 2006 - 2:45 pm UTC

This is what I looking for !

Thanks
Koshal

Using Query in trigger WHEN condition

Megala, January 25, 2008 - 7:06 pm UTC

Tom,

Is it possible to use "Query" in WHEN condition of the TRIGGER clause with OR condition:


This trigger (AFTER INSERT OR UPDATE ROW) should fire only on the following condition :

when :NEW.<COLUMN> IS NOT NULL OR (SELECT ... FROM OTHER TABLE WHERE <CONDITION> IS TRUE) ;

DOES Trigger support this construct in where condition.

Oracle 10.2.0.1

Thanks
Tom Kyte
January 28, 2008 - 6:59 am UTC

It does not and be careful with that.

It looks like you have intertable dependencies. If something exists in another table (t2) and I create a row in this table (t1) then something has to be true.

That would imply that tables T1 and T2 must be mutually exclusive from an access perspective - any operation on T2 would involve locking T1 and vice versa.

Do you know why?

column trigger

sam, June 14, 2011 - 9:14 pm UTC

TOm:

just curious on why you advised to use "when" caluse to filter the trigger update when you can do

CREATE OR REPLACE TRIGGER test
BEFORE UPDATE OF USER_ID ON test
FOR EACH ROW


Is not this more effective that using WHEN clause. I did not realize you can select columns in the update statement.


Figure 17-3

http://download.oracle.com/docs/cd/B10500_01/server.920/a96524/c18trigs.htm

Tom Kyte
June 17, 2011 - 11:58 am UTC

you can use the update of, but you need the when clause as well in order to capture only the changes.



How to prevent update on a Column.

Rajeshwaran, Jeyabal, April 21, 2012 - 9:17 am UTC

Tom:

I got an requirement to not allow the users to update a column (say the column x) in table 'T' (even the owner should not).
create table t(x number,y date);
insert into t (x,y) values(1,sysdate);
commit;

If i need to make this table such that no modifications to be done. I can make it a read-only in 11g or enable a check constraint like below.
alter table t read only;
alter table t add constraint t_chk check(1=0) enable novalidate;

How can i defined a check such that any user (even the owner) can insert the records into column 'x' but no update should be done on column 'x'? Is that something can be achieved without using Triggers?
Tom Kyte
April 22, 2012 - 9:10 am UTC

you cannot really truly prevent the owner. Unless you lock the owners account.


Never grant anything on that column (do not grant update)
Do not put code into the schema owner
Lock the schema owner except for upgrades


sure, you could use a trigger and make everything slower for everyone (and still not solve the problem, I'm the owner, I'll just disable it).


I'd go with

do not grant
do not have code in owners schema (since that doesn't need grant, table schemas shouldn't have code anyway in most cases)
do not allow owner to be a schema you log in as - it isn't necessary.

DBA's with "update any table" will of course be able to bypass that, but then again, they'd be able to bypass the trigger too - so so what.

And audit, enable auditing so you can detect a modification
And have undo retention set high so you can see a modification or use total recall.

Trigger Execution to supress

Rajeshwaran, Jeyabal, November 02, 2012 - 1:55 am UTC

Tom,

We have an 3rd party application that has triggers (nearly six for insert or update action) for deriving some logic
( i know you hate triggers & saw that in your slides "Triggers Trickery" )

Now we are about to develope a new module for this application. Where we are going to update some tables (say T, where Trigger exists) based on PK
which we read from a file.

We dont want (logic inside) the Trigger to be executed for this new-module (as per business use-case), so i took the following steps.

1) Define a new application context APP_CTX
2) set dbms_session.SET_CONTEXT('APP_CTX','FLAG','Y') as very first statement in new Package about to develope.
3) Redefine the trigger as " WHEN (SYS_CONTEXT('MY_CTX','FLAG') IS NULL) "

In this way, I am not disturbing the exising application and silently supress the Trigger execution for this new development.
a) This new module will be executed during business offhours (say 8pm -4am) monthly once.
b) we will establish a connection, run this package once in a month and disconnect. we dont do any more stuff in that same session/connection.

Do you see/feel any flaw in this logic (or) any better way of doing this?

If you need i can show my script, which i used for testing.
Tom Kyte
November 05, 2012 - 8:32 am UTC

does the 3rd party application owner care you are doing this? Many 3rd party software owners would say "you are no longer supported if you do that"

Trigger Execution to supress

Rajeshwaran, Jeyabal, November 05, 2012 - 8:52 pm UTC

does the 3rd party application owner care you are doing this? Many 3rd party software owners would say "you are no longer supported if you do that"

We have informed them and they are fine with this. I would like to know your thoughts.
Tom Kyte
November 06, 2012 - 7:45 am UTC

it seems risky, I wouldn't let you do it. The trigger are there for a purpose - you are bypassing that purpose. It could have serious downstream impact.

This seems like an enhancement request the vendor should be making - as part of their supported software offering.

yet another reason I like XAPI's (transactional API's) to data, instead of triggers, they could just code the logic and allow you to use the same api to access the data.

Trigger Exection to supress

Rajeshwaran, Jeyabal, November 06, 2012 - 8:27 pm UTC

Tom,
This seems like an enhancement request the vendor should be making

I agree, they need "UPDATE OF col1, col2" trigger but what they have is "UPDATE" trigger. We have requested them to make code change but they are not willing to make changes.

The code which we are about to develope is a new application, which will be integrated with them and we are about to do an update like this
update t set col3 = 'xxx' where pk = :b1

Which will be impacted by their triggers, so we decided to bypass them in such a way that they will not be impacted by us.
Tom Kyte
November 07, 2012 - 7:41 am UTC

I still stand by my statement:

this is a change the vendor should be making, period. it is their application, you are an end user.


instead of rewriting their logic, why don't you just recreate the trigger with the when condition??? why have it fire and have your logic short circuit it when you could just have it "not fire"???

Auditing a SELECT

Sri, November 14, 2012 - 2:27 pm UTC

Tom

I remember reading a document in this site about capturing audit information on users that perform a SELECT on restricted columns, but do not remember how I came across. Do you have an example?

If C was a restricted column containing sensitive data and some one ran the following query,

SELECT A,B,C
FROM TAB1;

then I need to capture the following information:
1. Who ran the query
2. The table (optional)
3. The Column (optional)

Is 2 and 3 available from somehere as opposed to hard coding it?

Is the query itself stored somewhere?

Thanks

Tom Kyte
November 14, 2012 - 8:31 pm UTC

dbms_fga, read about it.

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