Search code examples
oracle-databaseplsqltriggersoracle12cmutating-table

Copy row to history table before update without enumerating column names in PL/SQL trigger?


I am trying to create a PL/SQL trigger that copies the current version of a row into a history table when the row is updated. This can be easily done like this:

CREATE OR REPLACE TRIGGER foo BEFORE UPDATE ON bar FOR EACH ROW
BEGIN
  INSERT INTO bar_history VALUES bar.id = :old.id, bar.col1 = :old.col1 /* ...and so on */;
END;

However, I would like to avoid enumerating all the column names since the tables bar and bar_history are identical and I don't want to update the trigger every time I change the table. I have tried two approaches, none of which are working. Is there any other way to solve this?

Approach 1:

CREATE OR REPLACE TRIGGER foo BEFORE UPDATE ON bar FOR EACH ROW
BEGIN
  INSERT INTO bar_history VALUES :old;
END;

Since you apparently can not use :old as a rowtype (see this question) I get the following error message:

PLS-00049: bad bind variable 'OLD'

Approach 2:

CREATE TRIGGER foo BEFORE UPDATE ON bar FOR EACH ROW
BEGIN
  INSERT INTO bar_history
  SELECT * FROM bar WHERE id = :old.id;
END;

This also gives me an error:

ORA-04091: BAR is mutating, trigger/function may not see it


Solution

  • Some people have run into this situation before. Their approach - use package global variables to store rowid and dynamically select column names in after statement trigger. https://community.oracle.com/message/370167

    Tom Kyte also has some advice on this

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