Oracle 将数据插入表后使用触发器更新列

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

我试图使用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 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