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

时间:2017-04-23 00:45:01

标签: oracle stored-procedures oracle11g triggers

我试图使用插入触发器后更新一些数据,但我对语法感到困惑,不知道我做错了什么。任何人都可以分享你的意见。谢谢。

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 
;

1 个答案:

答案 0 :(得分:1)

如果可能在您的使用案例中,我建议您切换到BEFORE TRIGGER并改变传入的:NEW.FINE,前提是没有多个触发器会干扰彼此。如果AFTER对您不适用,我将在下面进一步列出BEFORE个替代方案。

使用BEFORE TRIGGER可以避免更新整个表的成本,并避免在其触发器中附加表上的任何UPDATE发生的变异表异常,并允许触发器在INSERT OR UPDATE

以下是一个例子:

首先创建测试表(可能这里有关于借用项目的其他列。)

CREATE TABLE BORROWED_BY (
  DUEDATE    DATE,
  RETURNDATE DATE,
  FINE       NUMBER
);

然后创建触发器(我建议截断日期{或者将某些四舍五入到混音中},这样您的FINE就是整数):

CREATE OR REPLACE TRIGGER CALCULATE_FINE_AMT
BEFORE INSERT OR UPDATE ON BORROWED_BY
FOR EACH ROW
  BEGIN
    :NEW.FINE := CASE WHEN (:NEW.DUEDATE - :NEW.RETURNDATE < 0)
      THEN ABS(TRUNC(:NEW.DUEDATE) - TRUNC(:NEW.RETURNDATE)) * 5
                ELSE 0 END;
  END;
/

然后测试一下:

按时返回

INSERT INTO BORROWED_BY (DUEDATE, RETURNDATE) 
VALUES (SYSDATE - 10, SYSDATE - 20);

SELECT * FROM BORROWED_BY;

DUEDATE    RETURNDATE  FINE  
13-APR-17  03-APR-17   0     

然后更新它:

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    

如果这确实必须是AFTER触发器,那么会有更高的成本关联,我建议您只切换到AFTER INSERT而不是INSERT OR UPDATE以避免递归触发器执行。正如其他人在评论中指出的那样,您需要切换到语句级(或复合)触发器。

以下是一个例子:

作为语句级触发器。这很昂贵(并且可能与其他触发器有不良的交互等),因为它会在每个语句后更新整个表:

编辑:添加语句级触发器更新现有数据的演示。

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;
/

并添加新数据:

INSERT INTO BORROWED_BY VALUES (SYSDATE, SYSDATE, NULL);

检查旧记录和新记录:

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  

所有记录的FINE都已更新。

结束修改

如果表变大,您可以(可能始终建议使用基准测试)通过复合触发器(稍微)降低成本。这是一个一般的例子,但我建议比较替代方案和调整您的用例。

使用主键重新创建表:

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