Oracle 将数据插入表后使用触发器更新列
我试图使用after-insert触发器更新一些数据,但我对语法感到困惑,不确定我做错了什么。有人能分享你在这方面的意见吗。多谢各位Oracle 将数据插入表后使用触发器更新列,oracle,stored-procedures,oracle11g,triggers,Oracle,Stored Procedures,Oracle11g,Triggers,我试图使用after-insert触发器更新一些数据,但我对语法感到困惑,不确定我做错了什么。有人能分享你在这方面的意见吗。多谢各位 Trigger code:- CREATE OR REPLACE TRIGGER calculate_fine_amt AFTER INSERT OR UPDATE ON BORROWED_BY FOR EACH ROW BEGIN UPDATE BORROWED_BY B SET B.FINE = CASE WH
Trigger code:-
CREATE OR REPLACE TRIGGER calculate_fine_amt
AFTER INSERT OR UPDATE ON BORROWED_BY
FOR EACH ROW
BEGIN
UPDATE BORROWED_BY B
SET B.FINE = CASE WHEN B.DUEDATE-B.RETURNDATE < 0
THEN ABS( B.DUEDATE-B.RETURNDATE )*5
ELSE 0
END
END
;
触发代码:-
创建或替换触发器计算\u罚款\u金额
插入或更新借来的\u之后
每行
开始
由B更新借来的U
设置B.FINE=当B.DUEDATE-B.RETURNDATE<0时的情况
然后是ABS(B.DUEDATE-B.RETURNDATE)*5
其他0
结束
结束
;
如果可能,在您的用例中,我建议在触发器之前切换到,并对传入的:NEW.FINE
进行变异,前提是没有多个触发器相互干扰。如果之前的对您不可行,我将在下面进一步概述之后的备选方案
在触发器之前使用
可以避免更新整个表的成本,也可以避免在其触发器中随附表上的任何更新
可能发生的变异表异常,并允许在插入或更新
时使用触发器
下面是一个例子:
首先创建测试表(这里大概还有其他关于借用项的列。)
然后创建触发器(我建议截断日期{或者在混合中进行一些舍入},这样你的FINE
s就是整数):
然后更新它:
UPDATE BORROWED_BY SET RETURNDATE = SYSDATE;
DUEDATE RETURNDATE FINE
13-APR-17 23-APR-17 50
然后再次更新:
UPDATE BORROWED_BY SET RETURNDATE = SYSDATE - 100;
SELECT * FROM BORROWED_BY;
DUEDATE RETURNDATE FINE
13-APR-17 13-JAN-17 0
迟交的新项目:
ROLLBACK;
INSERT INTO BORROWED_BY (DUEDATE, RETURNDATE)
VALUES (SYSDATE - 5, SYSDATE);
SELECT * FROM BORROWED_BY;
DUEDATE RETURNDATE FINE
18-APR-17 23-APR-17 25
如果这确实必须是一个后
触发器,那么相关的成本会更高,我建议您只切换到后插入
而不是插入或更新
,以避免递归触发器执行。正如其他人在评论中指出的,您需要切换到语句级(或复合)触发器
以下是一个例子:
作为语句级触发器。这代价很高(并且可能会与其他触发器发生不必要的交互等),因为它会在每条语句之后更新整个表:
编辑:添加语句级触发器更新现有数据的演示。
DROP TABLE BORROWED_BY;
CREATE TABLE BORROWED_BY (
DUEDATE DATE,
RETURNDATE DATE,
FINE NUMBER
);
添加一些不正确的初始数据:
INSERT INTO BORROWED_BY VALUES (SYSDATE - 100, SYSDATE, -1919);
INSERT INTO BORROWED_BY VALUES (SYSDATE - 200, SYSDATE, -1919);
INSERT INTO BORROWED_BY VALUES (SYSDATE - 300, SYSDATE, -1919);
COMMIT;
并验证:
SELECT * FROM BORROWED_BY;
DUEDATE RETURNDATE FINE
13-JAN-17 23-APR-17 -1919
05-OCT-16 23-APR-17 -1919
27-JUN-16 23-APR-17 -1919
然后创建触发器:
CREATE OR REPLACE TRIGGER CALCULATE_FINE_AMT
AFTER INSERT ON BORROWED_BY
BEGIN
UPDATE BORROWED_BY
SET FINE = CASE WHEN (DUEDATE - RETURNDATE < 0)
THEN ABS(TRUNC(DUEDATE) - TRUNC(RETURNDATE)) * 5
ELSE 0 END;
END;
/
并检查新旧记录:
SELECT * FROM BORROWED_BY;
DUEDATE RETURNDATE FINE
13-JAN-17 23-APR-17 500
05-OCT-16 23-APR-17 1000
27-JUN-16 23-APR-17 1500
23-APR-17 23-APR-17 0
所有记录都已更新。
DROP TABLE BORROWED_BY;
CREATE TABLE BORROWED_BY (
DUEDATE DATE,
RETURNDATE DATE,
FINE NUMBER
);
结束编辑
如果表变大了,您可以(可能的话,总是建议使用基准测试)通过复合触发器(稍微)降低成本。这里有一个通用的示例,但我建议您针对您的用例比较备选方案和调优
使用主键重新创建表:
CREATE TABLE BORROWED_BY (
BORROWED_BY_ID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
DUEDATE DATE,
RETURNDATE DATE,
FINE NUMBER
);
然后创建一个复合触发器:
CREATE OR REPLACE TRIGGER CALCULATE_FINE_AMT
FOR INSERT ON BORROWED_BY
COMPOUND TRIGGER
TYPE NUMBER_TAB IS TABLE OF NUMBER;
V_KEYS NUMBER_TAB;
BEFORE STATEMENT IS
BEGIN
V_KEYS := NUMBER_TAB();
END BEFORE STATEMENT;
AFTER EACH ROW
IS
BEGIN
V_KEYS.EXTEND;
V_KEYS(V_KEYS.COUNT) := :NEW.BORROWED_BY_ID;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
FOR BORROWED_POINTER IN 1..V_KEYS.COUNT
LOOP
UPDATE BORROWED_BY
SET FINE = CASE WHEN (DUEDATE - RETURNDATE < 0)
THEN ABS(TRUNC(DUEDATE) - TRUNC(RETURNDATE)) * 5
ELSE 0 END
WHERE BORROWED_BY_ID = V_KEYS(BORROWED_POINTER);
END LOOP;
END AFTER STATEMENT;
END CALCULATE_FINE_AMT;
/
真的吗?update语句中缺少分号。正在更新后触发器中更新同一个表。。。一个变异表异常似乎就是这样结束的。好吧,至少如果它在insert之后工作,那么它应该是好的。为什么不使用一个计划每天运行的作业呢?而不是在每次更改后更新整个表。是否可以对表中已插入的数据应用触发器?感谢@Teja Yes在本文的三个示例中,第二个(简单语句级触发器)将应用于表中已插入的数据。您可以通过创建示例表来测试这一点,添加一些应该有罚款但没有罚款的行,然后创建触发器并添加新数据。新数据和旧数据都将更新。或者,如果按照第一个示例中的建议切换到BEFORE触发器,则可以在创建触发器时一次性更新所有现有数据,以确保所有现有数据处于预期状态。这些选项对您有用吗?我尝试了第二个简单语句级触发器示例,但它不会更新表中的现有数据…谢谢@Teja,这很有趣。我不确定您为什么没有看到现有数据更新。我将用一个更新现有数据的例子来更新这篇文章,并在一分钟内跟进。然后,你能运行一个确切的例子并让我知道结果吗。谢谢你。。。我可以向你提供最新的详细信息。。。。对于正在进行的任何新插入,这两个触发器都可以正常工作。。。。但是现有的数据并没有像我前面提到的那样受到影响。。。
SELECT * FROM BORROWED_BY;
DUEDATE RETURNDATE FINE
13-JAN-17 23-APR-17 500
05-OCT-16 23-APR-17 1000
27-JUN-16 23-APR-17 1500
23-APR-17 23-APR-17 0
CREATE TABLE BORROWED_BY (
BORROWED_BY_ID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
DUEDATE DATE,
RETURNDATE DATE,
FINE NUMBER
);
CREATE OR REPLACE TRIGGER CALCULATE_FINE_AMT
FOR INSERT ON BORROWED_BY
COMPOUND TRIGGER
TYPE NUMBER_TAB IS TABLE OF NUMBER;
V_KEYS NUMBER_TAB;
BEFORE STATEMENT IS
BEGIN
V_KEYS := NUMBER_TAB();
END BEFORE STATEMENT;
AFTER EACH ROW
IS
BEGIN
V_KEYS.EXTEND;
V_KEYS(V_KEYS.COUNT) := :NEW.BORROWED_BY_ID;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
FOR BORROWED_POINTER IN 1..V_KEYS.COUNT
LOOP
UPDATE BORROWED_BY
SET FINE = CASE WHEN (DUEDATE - RETURNDATE < 0)
THEN ABS(TRUNC(DUEDATE) - TRUNC(RETURNDATE)) * 5
ELSE 0 END
WHERE BORROWED_BY_ID = V_KEYS(BORROWED_POINTER);
END LOOP;
END AFTER STATEMENT;
END CALCULATE_FINE_AMT;
/
INSERT INTO BORROWED_BY (DUEDATE, RETURNDATE) VALUES (SYSDATE - 5, SYSDATE);
INSERT INTO BORROWED_BY (DUEDATE, RETURNDATE) VALUES (SYSDATE - 20, SYSDATE - 10);
INSERT INTO BORROWED_BY (DUEDATE, RETURNDATE) VALUES (SYSDATE - 30, SYSDATE - 40);
SELECT *
FROM BORROWED_BY;
BORROWED_BY_ID DUEDATE RETURNDATE FINE
4 18-APR-17 23-APR-17 25
5 03-APR-17 13-APR-17 50
6 24-MAR-17 14-MAR-17 0