【问题标题】:Oracle Trigger - table name is mutating, trigger/function may not see itOracle 触发器 - 表名发生变化,触发器/函数可能看不到它
【发布时间】:2015-05-26 15:21:31
【问题描述】:

我有一个具有以下场景(简化)的 Oracle 数据库:

Projects                      Params
-------------                 -----------
PROJ_ID     SCODE             PARAM_ID PARAM_TYPE PROJ_ID PARAM_VALUE
1000        123               5000       4614     1000    '00'
1001        124               5001       4610     1000    'Micro'
1002        123               5002       4614     1001    '02'
                              5003       4614     1002    '01'

这意味着,3 个项目 - 项目 1000 有 2 个不同的参数,第二个和第三个项目各有一个参数。

现在我需要在项目表上编写一个触发器,它会自动在 params 表中插入一个新行,其中参数的最高值为 +1,类型为“4614”,具有给定的 SCODE。

INSERT INTO Projects VALUES (1003,123) 

...应该触发事件

INSERT INTO Parameters VALUES (5004,4614,1003,'02')

现在我有两种可能性,但都不起作用:

如果触发器声明为“BEFORE”,我无法在参数表中插入,因为外键约束向我显示一个错误,即 Project 1003 尚未创建。无法在触发器内执行 Commit。

如果触发器被声明为“AFTER”,我得到一个错误

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

因为我正在访问现在触发的表。

这个问题必须有任何解决方案。 任何帮助表示赞赏!

//编辑

我的触发器:

CREATE OR REPLACE TRIGGER PROJ_ARI_TRIGGER
AFTER INSERT
ON PROJECTS
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
v_param VARCHAR2(10);
BEGIN
      v_param := get_next_param_val(:new.SCODE);
      INSERT INTO Parameters(<<sequence>>,4614,:new.PROJ_ID,v_param);
END PROJ_ARI_TRIGGER;

函数 get_next_param_val 只是选择项目表并返回正确的参数值。

【问题讨论】:

  • 您可以更改外键,使其可延期;这样,在您提交之前不会对其进行检查。请记住,如果您进行批量更新,这可能会导致问题 - 在事务结束之前您不会收到错误。或者,不是直接插入到表中,而是通过存储过程来完成。这样,您就可以正确控制逻辑和流程,而无需做任何花哨的变通方法。
  • @Boneist 不幸的是,我无法更改数据库系统的约束。为什么将 INSERT 语句放入存储过程会有所帮助?我的触发器触发器的内容就像 v_new_param := calculate_param();插入参数值(....,new.proj_id,v_new_param)。
  • 如果您要编辑问题以添加脚本来创建触发器会更好。
  • 我不明白你试图描述的算法。您正在尝试获取 param_value 值,对吗?为什么你会得到02 的值?如果我们假设字符串值始终可以转换为数字,那不是当前数据的 max()+1 似乎是03。如果您正在执行 max()+1,您就会明白如果系统中允许多个用户,您将生成重复项,对吧?

标签: oracle triggers mutating-table


【解决方案1】:

该死的变异表!是时候打包了。

CREATE OR REPLACE PACKAGE no_mutation_pkg
AS
   procedure set_val(pVal number);
   function get_val return number;
END;
/

CREATE OR REPLACE PACKAGE BODY no_mutation_pkg
AS
   g_val number;
   procedure set_val(pVal number)
   IS
     BEGIN
       g_val := pVAl;
   END;
   function get_val return number
   IS BEGIN
      return g_val;
   end;
   END;

然后您创建一个 BEFORE 插入触发器,该触发器计算该值并使用 set_val 将其存储在包中,而 AFTER 插入触发器使用 GET_VAL 检索该值并执行插入。

编辑:根据贾斯汀对重复项的评论,您肯定需要考虑这一点。坦率地说,当您需要跳过这些障碍时,通常会怀疑数据架构

【讨论】:

  • after 触发器必须是 statement-level。
  • 好收获。谢谢!我今天不在办公室,所以我在即时打字。
【解决方案2】:

我找到了解决问题的方法!

使用复合触发器,我可以先得到SCODE中的最大值并计算参数值(每行之前)

然后在参数表中插入值(每行之后)

详情:Compound Trigger Example

谢谢@鲍勃·贾维斯

【讨论】:

  • Per Justins comment above, if this is a multi-user system you might want to test to ensure that you arent 如果两个人执行类似的插入,就会得到重复。例如对于上面的数据集,让一个人使用 scode 123 插入项目 1003,然后让另一个人使用 scode 123 插入项目 1004,然后两个人都进行提交。
猜你喜欢
  • 2016-08-02
  • 1970-01-01
  • 2021-12-20
  • 2022-10-20
  • 1970-01-01
  • 2011-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多