【问题标题】:why my "after insert on" trigger not considering all other inserts after the trigger is created?为什么我的“插入后”触发器在创建触发器后不考虑所有其他插入?
【发布时间】:2020-05-10 09:20:09
【问题描述】:

这里是概念模型: 在司机表中,totalTripMade 属性全部为空,但行程表包含司机进行的所有行程,所以我需要创建一个触发器,以便在行程中插入一条记录时,触发器将通过计算所有行程来更新 totalTripMade使用 l# 的行程

create or replace trigger updateTrip
   AFTER INSERT 
     on TRIP
     for each row
      declare
         PRAGMA AUTONOMOUS_TRANSACTION;
         totalTrips number;
      begin

     UPDATE DRIVER 
     SET TOTALTRIPMADE = (SELECT COUNT(L#) FROM TRIP WHERE L# =:NEW.L#)
     WHERE L#=:NEW.L#;

         SELECT COUNT(L#) into totalTrips FROM TRIP WHERE L# =:NEW.L#;
         DBMS_OUTPUT.PUT_LINE(:NEW.L# || ' ' || totalTrips);
   commit;
end;
/

SHOW ERRORS;

select * from DRIVER;
--
insert into TRIP values(109, 10001, 'SST005', sysdate );
insert into TRIP values(110, 10001, 'SST005', sysdate );
insert into TRIP values(111, 10001, 'SST005', sysdate );


--
select * from DRIVER;
select * from TRIP;

所以驱动程序 10001 在这 3 个插入语句之前进行了 27 次行程,因为基于 l# 10001 有 27 条记录,但在每次插入之后,此代码 "DBMS_OUTPUT.PUT_LINE(:NEW.L# || ' ' | | 总行程);"一直给我 27 3 次,应该是 28、29 然后 30,请问为什么我的触发器没有正确计算行程表中基于 l# 的记录?

这是运行这些插入语句后的输出:

SQL> insert into TRIP values(109, 10001, 'SST005', sysdate );
10001 27

1 row created.

SQL> insert into TRIP values(110, 10001, 'SST005', sysdate );
10001 27

1 row created.

SQL> insert into TRIP values(111, 10001, 'SST005', sysdate );
10001 27

1 row created.

【问题讨论】:

    标签: plsql triggers sqlplus database-trigger


    【解决方案1】:

    我认为错误就在这里:

    我需要创建一个触发器,这样当一条记录插入到行程中时,触发器将通过使用 l# 计算所有行程来更新 totalTripMade

    我认为您不需要创建这样的触发器。如果您需要统计司机的总行程次数,请直接使用SELECT COUNT(*) FROM TRIP WHERE L# = <driver_number> 之类的方式查询行程表。即使您有很多行,它也不会减慢数据库速度,只要您在 L# 列上有索引。

    使用这样的触发器不是一个好主意的原因有很多。首先,如果同时插入多行会发生什么?如果插入 10 行,触发器应该触发 10 次,还是最后只触发一次?

    另外,如果您更新TRIP 表中的一行以更改驱动程序编号,或删除此表中的一行,会发生什么情况?您在处理这些案件吗?

    但是你不应该做这样的事情的最大原因是它破坏了并发性。假设一名司机在数据库中记录了 27 次行程。假设您正尝试向该司机添加三个行程,并且其他人正尝试向同一司机添加三个不同的行程同时。在您提交之前,你们都不能看到彼此的行程,你们都只能看到已经存在的 27 次行程。在您的会话中,触发器会尝试将此驱动程序的总行程设置为 30,因为您的会话可以看到 27 个现有行程和您的三个新行程,并且在其他人的会话中,触发器还将尝试设置总数此司机的行程也增加到 30 次,因为此会话会看到 27 次现有行程及其三个新行程,但不是您的三个新行程。一旦双方都提交了,驱动程序在数据库中就有 33 次行程,但他们的 totalTripsMade 错误地显示为 30。

    回答你的问题

    我可以知道为什么我的触发器没有正确计算行程表中基于 l# 的记录吗?

    我猜这是因为在处理此触发器时您遇到了 ORA-04091 table is mutating 错误,在 Google 搜索结果中看到了此错误,说要使用 PRAGMA AUTONOMOUS_TRANSACTION,因此您将其添加到触发器中并且错误消失了离开。此 pragma 导致您的触发器在独立会话中运行:它看不到您插入的行程,因此它打印 27,因为它只能看到已经存在的行程。

    复合触发器是在不使用PRAGMA AUTONOMOUS_TRANSACTION 的情况下避免ORA-04091 错误的一种方法。有一个使用复合触发器here 的示例。但是,您还必须为UPDATEDELETE 编写类似的触发器,并且复合触发器无法解决我上面提到的并发问题。

    但是,我建议去掉这个触发器(可能还有 DRIVER 表中的 totalTripMade 列)。如果您需要计算司机的行程次数,请通过计算TRIP 表中的行数来完成。要求您删除您的代码可能会让我觉得很苛刻,但最终您正在尝试做一些不起作用的事情。我在 StackOverflow 上看到过其他问题,人们试图做类似的事情,所以你并不孤单尝试这样的事情。在直观的层面上,将这些计数添加到表中似乎是一件合理的事情。但是,一旦您了解了数据库的工作原理,就会发现其中的缺陷。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多