可以从Oracle中的触发器调用动态SQL吗?

时间:2012-09-25 18:23:17

标签: sql oracle plsql triggers dynamic-sql

我有十几张表,我想保留这些变化的历史。对于每一个我创建了第二个表,结尾是_HISTO,并添加了字段modtime,action,user。

在我插入,修改或删除此表中的记录之前,我调用(来自我的delphi应用程序)一个oracle过程,将实际值复制到histo表,然后执行操作。

我的程序通过DBA_TAB_COLUMNS生成动态sql,然后执行生成的(insert into tablename_histo(fields s)select fields,sysdate,'acition',userid from table_name

我被告知我无法从触发器调用此过程,因为它必须选择触发触发器的表。这是真的 ?是否有可能实现我的需要?

2 个答案:

答案 0 :(得分:2)

假设您希望使用触发器维护历史记录(而不是在Oracle中跟踪历史数据的任何其他方法 - 工作区管理器,全部调用,流,精细审计等),您可以在触发器中使用动态SQL。但是动态SQL遵循与静态SQL相同的规则。甚至行级触发器中的静态SQL通常也不能查询定义触发器的表而不会生成变异表异常。

但是,您可以使用相同的数据字典表,首先编写一些生成触发器的动态SQL,而不是从触发器调用动态SQL。触发器本身会静态引用:new.column_name:old.column_name。当然,您必须编辑触发器或重新运行在添加新列时动态创建触发器的过程。因为大概需要将列添加到主表和历史表中,所以这通常不是太大的事情。

答案 1 :(得分:0)

Oracle不允许触发器对定义触发器的表执行SELECT。如果你尝试它,你会得到可怕的“变异表”错误(ORA-04091),虽然有办法解决 错误,但它们增加了很多复杂性,价值不大。如果你真的想在每次更新表时构建一个动态查询(IMO从性能的角度来看这是一个坏主意 - 我发现元数据查询通常很慢,但是YMMV)它最终应该看起来像

strAction := CASE
               WHEN INSERTING THEN 'INSERT'
               WHEN UPDATING THEN 'UPDATE'
               WHEN DELETING THEN 'DELETE'
             END;

INSERT INTO TABLENAME_HISTO
  (ACTIVITY_DATE, ACTION, MTC_USER,
   old_field1, new_field1, old_field2, new_field2)
VALUES
  (SYSDATE, strAction, USERID,
   :OLD.field1, :NEW.field1, :OLD.field2, :NEW.field2)

分享并享受。