views:

4059

answers:

4

Inside of a trigger I'm trying to loop over all columns on a table and compare the new values to the old values. Here is what I have so far:

CREATE OR REPLACE TRIGGER "JOSH".TEST#UPD BEFORE 
UPDATE ON "JOSH"."TEST_TRIGGER_TABLE" REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW
declare    
   oldval varchar(2000);   
   newval varchar(2000);   
begin    
   for row in (SELECT column_name from user_tab_columns where table_name='TEST_TRIGGER_TABLE') loop  
     execute immediate 'select :old.'||row.column_name||' from dual'   into oldval;  
     execute immediate 'select :new.'||row.column_name||' from dual'   into newval;  
     --Do something here with the old and new values
   end loop;  
end;

The trigger compiles, but when the trigger fires, I'm getting "ORA-01008: not all variables bound" on the first execute immediate because it's expecting a value for :old. :old and :new are already defined as part of the trigger, but it appears that execute immediate can't see those variables.

Is there a way to dynamically iterate over the column values in a trigger?

+3  A: 

I'm not sure if you can do what you are trying to do. What is the reason you don't want to explicitly name the table columns inside the PL/SQL code? If the table fields are changing often, you could build PL/SQL that dynamically builds the PL/SQL trigger for each table (with the explicit field names in each). Each time the table changes, you could run that PL/SQL to generate the new trigger.

Shane
I was kind of afraid that this might be the case. I was just trying to keep the code as simple as possible and avoid a big mess of the same code over and over.
Josh Bush
+2  A: 

I had a similar problem, although in MSSQL.

My solution was to write a stored procedure which iterates through tables and columns information (either via dictionary views or a custom repository) and generates the required triggers. The procedure needs to be run only if the data model changes.

The advantage is that you don't have to cursor through the metamodel in each update, but rather generate your triggers in advance.

devio
+5  A: 

No, you cannot reference :old and :new values dynamically. As Shane suggests, you can write code to generate the static trigger code, if that makes life easier. Also, you can make "do something here" into a package procedure so that your trigger becomes:

CREATE OR REPLACE TRIGGER JOSH.TEST#UPD BEFORE 
UPDATE ON JOSH.TEST_TRIGGER_TABLE
begin    
   my_package.do_something_with (:old.col1, :new.col1);
   my_package.do_something_with (:old.col2, :new.col2);
   my_package.do_something_with (:old.col3, :new.col3);
   -- etc.
end;

(You can ditch the pointless REFERENCING clause by the way).

Tony Andrews
+3  A: 

Are you essentially trying to build your own system to audit all changes to the table? (My best guess as to what you might be doing with the old and new values of arbitrary columns.) If so, you might want to look into Oracle's own auditing capabilities.

Dave Costa