【问题标题】:Oracle trigger update of column's value in Table1 after DML on column in Table2在表 2 中的列进行 DML 之后,Oracle 触发更新表 1 中的列值
【发布时间】:2014-05-14 19:12:28
【问题描述】:

朋友们! 我有两张桌子(tb_1,tb_2)。

SQL> DESC tb_1;
 Name                                      Null?    Type
 ----------------------------------------- -------- -----------
 PK_ID                                     NOT NULL NUMBER
 SUM_QUANTITY                                       NUMBER

SQL> DESC tb_2;
 Name                                      Null?    Type
 ----------------------------------------- -------- -----------
 FK_ID                                              NUMBER
 QUANTITY                                           NUMBER

列 FK_ID - 列 PK_ID 上的外键。 SUM_QUANTITY 列 - QUANTITY 列中特定 pk_id 的值的总和

例如:

SQL> SELECT * FROM tb_1;
 PK_ID SUM_QUANTITY
------ ------------
     1            8
     2           18
     3            2

SQL> SELECT * FROM tb_2;
FK_ID   QUANTITY
----- ----------
    1          1
    1          3
    1          4
    2          5
    2          6
    2          7
    3          1
    3          1

我想在 SUM_QUANTITY 列之后自动更新 tb_1 中的值 tb_2 中针对特定 PK_ID 的 DML(INSERT、UPDATE OF quantity、DELETE)操作!

我已经创建了触发器:

CREATE OR REPLACE TRIGGER t_aiud
AFTER INSERT OR UPDATE OF quantity OR DELETE ON tb_2
BEGIN
UPDATE tb_1 SET sum_quantity =  (SELECT sum(quantity) FROM tb_2 WHERE tb_1.pk_id = tb_2.fk_id);
END;

它适用于表 tb_1 中的所有 pk_id!那么,如何在 DML 操作后自动更新特定 pk_id 列 SUM_QUANTITY 中 tb_1 中的值?

谢谢!

【问题讨论】:

  • 限制 fk_id 使用 WHEN 子句。要更新特定的 pk_id,请在更新中添加 where 子句。
  • 您在这里找到解决方案了吗?我面临同样的情况。

标签: oracle plsql oracle11g


【解决方案1】:

如果您想引用表格数据,您就不能轻易地做到这一点。如果您尝试基于where tb1_1.pk_id = :new.fk_id 限制更新,则必须将其设置为for each row 触发器,然后您将获得a mutating table error,可以对其进行编码,但它并不漂亮或令人愉快,或者通常是必要的.

您现在所做的工作在一定程度上会起作用,但是如果两个人同时修改 tb_2,即使他们使用的不是同一个 fk_id,您也会得到不可预知的结果,因为他们每个人的触发触发只会看到他们自己的变化 - 所以sum_quanity 可能最终得到错误的值。如果将您的三条记录插入tb_1

insert into tb_1 values (1, 0);
insert into tb_1 values (2, 0);
insert into tb_1 values (3, 0);
commit;

然后在一个会话中插入前七个 tbl_2 行而不提交:

insert into tb_2 values (1, 1);
insert into tb_2 values (1, 3);
insert into tb_2 values (1, 4);
insert into tb_2 values (2, 5);
insert into tb_2 values (2, 6);
insert into tb_2 values (2, 7);
insert into tb_2 values (3, 1);

select * from tb_1;

     PK_ID SUM_QUANTITY
---------- ------------
         1            8 
         2           18 
         3            1 

然后在另一个会话中插入最后一行:

insert into tb_2 values (3, 1);
commit;

...这将阻塞直到第一个会话被提交,然后这个插入将发生,但总数将是错误的:

commit;
select * from tb_1;
     PK_ID SUM_QUANTITY
---------- ------------
         1
         2
         3            1

它会在随后的修改中自行解决,但在任何给定时间都可能出错。

您可以根据正在修改的值直接更新tb_1

CREATE OR REPLACE TRIGGER t_aiud
AFTER INSERT OR UPDATE OF quantity OR DELETE ON tb_2
FOR EACH ROW
BEGIN
  UPDATE tb_1
  SET sum_quantity = sum_quantity
    + NVL(:NEW.quantity, 0) - NVL(:OLD.quantity, 0)
  WHERE pk_id = NVL(:NEW.fk_id, :OLD.fk_id);
END;
/

... 不需要查询触发器所在的表,所以不会出现 mutating table 错误;但如果两个会话正在修改同一个键的数据,这也会阻塞。

除非这始终是一个单用户系统(这不太可能),否则您最好使用将两个表连接在一起的视图即时计算总和,或者对于大量数据,有一个物化视图,可以为您维护总数。

【讨论】:

  • 嗨,亚历克斯,谢谢!我有同样的看法,但 Guneli 的解决方案有效..hmmm
【解决方案2】:

试试这个:

CREATE OR REPLACE TRIGGER t_aiud
AFTER INSERT OR UPDATE OF quantity OR DELETE ON tb_2
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  UPDATE tb_1
  SET sum_quantity =
    (SELECT SUM(quantity) FROM tb_2 WHERE tb_1.pk_id = tb_2.fk_id
    )
  WHERE pk_id=:NEW.fk_id;
  COMMIT;
END;

【讨论】:

  • :NEW.pk_id 无效。 tb_2 中的 where 子句:NEW.fk_id 有效
  • 感谢 Guneli 和 Rock'em! PRAGMA AUTONOMOUS_TRANSACTION 是一个键,嗯。
  • 但是自主触发器看不到你刚刚修改的数据,所以总数没有变化? (Demo)
猜你喜欢
  • 2013-11-22
  • 2016-06-17
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 2015-12-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多