plsql - 为什么我的“插入后”触发器在创建触发器后不考虑所有其他插入?
问题描述
下面是概念模型: 在驱动表中,totalTripMade 属性全部为空,但行程表包含了驱动程序进行的所有行程,所以我需要创建一个触发器,这样当一条记录插入到行程中时,触发器将通过使用 l# 计算所有行程来更新 totalTripMade
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# || ' ' || totalTrips) ;" 一直给我 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.
解决方案
我认为错误在这里:
我需要创建一个触发器,以便在将记录插入行程时,触发器将通过使用 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
错误,看到这个错误的谷歌搜索结果说使用PRAGMA AUTONOMOUS_TRANSACTION
,所以你将它添加到你的触发器中并且错误消失了。此 pragma 导致您的触发器在独立会话中运行:它看不到您插入的行程,因此打印 27 因为它只能看到已经存在的行程。
复合触发器是一种ORA-04091
无需使用即可避免错误的方法PRAGMA AUTONOMOUS_TRANSACTION
。这里有一个使用复合触发器的例子。但是,您还必须为UPDATE
and编写类似的触发器DELETE
,并且复合触发器无法解决我上面提到的并发问题。
但是,我建议去掉这个触发器(也可能还有表中的totalTripMade
列DRIVER
)。如果您需要计算司机的行程次数,请通过计算TRIP
表中的行数来完成。要求你删除你的代码可能会让我觉得很苛刻,但最终你是在尝试做一些行不通的事情。我在 StackOverflow 上看到了人们尝试做类似事情的其他问题,所以你并不孤单尝试这样的事情。在直观的层面上,将这些计数添加到表中似乎是一件合理的事情。但是,一旦您了解了数据库的工作原理,就会发现其中的缺陷。
推荐阅读
- c# - 错误无法从“out string”转换为“out int”
- linux - 安装 git 时,是什么导致 git interactive 不存在?
- google-cloud-platform - 谷歌云功能可以监听多个事件触发器吗?
- c++ - 将 RFC5114 中定义的 1024bit P 和 G 值加载到 boost uint1024
- mongodb - MongoDB保留满足条件的路径
- laravel - 'Unexpected token <' 包装 .vue 文件时
new to vue here and I am creating an area in the code for all my vue components to be rendered through a
blade.php
file (sfc). I installed the cli usingvue create projectN
- mongodb - MongoDB Atlas Golang mgo package "No reachable servers" error
- python - 如何将一个类对象的 id 传递给 django views.py 中的另一个类对象
- json - 在 get_json_object 的搜索字符串中包括 $
- javascript - How to pass a date parameter into a Coinbase Fetch API call?