Oracle触发器用于根据触发表的总和更新表格。

huangapple go评论83阅读模式
英文:

Oracle trigger for updating table based on sum of triggering table

问题

我有以下的表数据结构:

  1. PARENT
  2. ID TOTAL_AMOUNT
  3. 101 10
  4. 102 15
  5. CHILDREN
  6. ID PARENT_ID AMOUNT
  7. 201 101 6
  8. 202 101 4
  9. 203 102 10
  10. 204 102 5

我想创建一个触发器,当修改CHILDREN表时,更新PARENT.TOTAL_AMOUNT为其子记录的总和。但是我似乎无法解决“表正在变异”的限制问题。

使用Oracle 11。

编辑:

以下是复制上述示例场景的代码。

  1. CREATE TABLE PARENTS (
  2. ID NUMBER PRIMARY KEY,
  3. TOTAL_AMOUNT NUMBER
  4. );
  5. CREATE TABLE CHILDREN (
  6. ID NUMBER PRIMARY KEY,
  7. PARENT_ID NUMBER REFERENCES PARENTS(ID),
  8. AMOUNT NUMBER
  9. );
  10. INSERT INTO PARENTS( ID, TOTAL_AMOUNT ) VALUES ( 101, 10 );
  11. INSERT INTO PARENTS( ID, TOTAL_AMOUNT ) VALUES ( 102, 15 );
  12. INSERT INTO CHILDREN ( ID, PARENT_ID, AMOUNT ) VALUES ( 201, 101, 6 );
  13. INSERT INTO CHILDREN ( ID, PARENT_ID, AMOUNT ) VALUES ( 202, 101, 4 );
  14. INSERT INTO CHILDREN ( ID, PARENT_ID, AMOUNT ) VALUES ( 203, 102, 10 );
  15. INSERT INTO CHILDREN ( ID, PARENT_ID, AMOUNT ) VALUES ( 204, 102, 5 );
  16. CREATE OR REPLACE TRIGGER TRG_TEST
  17. AFTER UPDATE OF AMOUNT ON CHILDREN
  18. FOR EACH ROW
  19. BEGIN
  20. UPDATE PARENTS
  21. SET TOTAL_AMOUNT = (
  22. SELECT SUM(AMOUNT)
  23. FROM CHILDREN
  24. WHERE PARENT_ID = :NEW.PARENT_ID
  25. );
  26. END;
  27. /

运行以下语句会产生错误:

  1. UPDATE CHILDREN SET AMOUNT = 11 WHERE ID = 204;
  2. Error starting at line : 44 in command -
  3. UPDATE CHILDREN SET AMOUNT = 11 WHERE ID = 204
  4. Error report -
  5. ORA-04091: table RMS.CHILDREN is mutating, trigger/function may not see it
  6. ORA-06512: at "RMS.TRG_TEST", line 3
  7. ORA-04088: error during execution of trigger 'RMS.TRG_TEST'
英文:

I have the following table data structure:

  1. PARENT
  2. ID TOTAL_AMOUNT
  3. 101 10
  4. 102 15
  5. CHILDREN
  6. ID PARENT_ID AMOUNT
  7. 201 101 6
  8. 202 101 4
  9. 203 102 10
  10. 204 102 5

I'd like to create a trigger that updates PARENT.TOTAL_AMOUNT to be the sum of its child records when CHILDREN is modified. But I can't seem to get around the "table is mutating" restriction.

Using Oracle 11.

EDIT:

Here is code to reproduce the sample scenario above.

  1. CREATE TABLE PARENTS (
  2. ID NUMBER PRIMARY KEY,
  3. TOTAL_AMOUNT NUMBER
  4. );
  5. CREATE TABLE CHILDREN (
  6. ID NUMBER PRIMARY KEY,
  7. PARENT_ID NUMBER REFERENCES PARENTS(ID),
  8. AMOUNT NUMBER
  9. );
  10. INSERT INTO PARENTS( ID, TOTAL_AMOUNT ) VALUES ( 101, 10 );
  11. INSERT INTO PARENTS( ID, TOTAL_AMOUNT ) VALUES ( 102, 15 );
  12. INSERT INTO CHILDREN ( ID, PARENT_ID, AMOUNT ) VALUES ( 201, 101, 6 );
  13. INSERT INTO CHILDREN ( ID, PARENT_ID, AMOUNT ) VALUES ( 202, 101, 4 );
  14. INSERT INTO CHILDREN ( ID, PARENT_ID, AMOUNT ) VALUES ( 203, 102, 10 );
  15. INSERT INTO CHILDREN ( ID, PARENT_ID, AMOUNT ) VALUES ( 204, 102, 5 );
  16. CREATE OR REPLACE TRIGGER TRG_TEST
  17. AFTER UPDATE OF AMOUNT ON CHILDREN
  18. FOR EACH ROW
  19. BEGIN
  20. UPDATE PARENTS
  21. SET TOTAL_AMOUNT = (
  22. SELECT SUM(AMOUNT)
  23. FROM CHILDREN
  24. WHERE PARENT_ID = :NEW.PARENT_ID
  25. );
  26. END;
  27. /

Running the following statement produces error:

  1. UPDATE CHILDREN SET AMOUNT = 11 WHERE ID = 204;
  2. Error starting at line : 44 in command -
  3. UPDATE CHILDREN SET AMOUNT = 11 WHERE ID = 204
  4. Error report -
  5. ORA-04091: table RMS.CHILDREN is mutating, trigger/function may not see it
  6. ORA-06512: at "RMS.TRG_TEST", line 3
  7. ORA-04088: error during execution of trigger 'RMS.TRG_TEST'

答案1

得分: 2

您最好使用VIEWMATERIALIZED VIEW来生成总计。

如果必须使用触发器(不建议),那么可以将触发器从使用AFTER ... EACH ROW更改为AFTER语句触发器:

  1. CREATE OR REPLACE TRIGGER TRG_TEST
  2. AFTER UPDATE OF AMOUNT ON CHILDREN
  3. BEGIN
  4. UPDATE PARENTS p
  5. SET TOTAL_AMOUNT = (
  6. SELECT SUM(AMOUNT)
  7. FROM CHILDREN c
  8. WHERE c.PARENT_ID = p.ID
  9. );
  10. END;
  11. /

如果您希望触发器更高效,并且仅更新修改的id,则可以使用复合触发器。

  1. CREATE OR REPLACE TRIGGER TRG_TEST
  2. FOR UPDATE OF AMOUNT ON CHILDREN
  3. COMPOUND TRIGGER
  4. TYPE ids_type IS TABLE OF PARENTS.ID%TYPE;
  5. ids ids_type := ids_type();
  6. AFTER EACH ROW
  7. IS
  8. BEGIN
  9. IF :OLD.parent_id IS NOT NULL AND :OLD.parent_id NOT MEMBER OF ids THEN
  10. ids.EXTEND;
  11. ids(ids.COUNT) := :OLD.parent_id;
  12. END IF;
  13. IF :NEW.parent_id IS NOT NULL AND :NEW.parent_id NOT MEMBER OF ids THEN
  14. ids.EXTEND;
  15. ids(ids.COUNT) := :NEW.parent_id;
  16. END IF;
  17. END AFTER EACH ROW;
  18. AFTER STATEMENT
  19. IS
  20. BEGIN
  21. FORALL i IN 1 .. ids.COUNT
  22. UPDATE parents
  23. SET total_amount = (SELECT SUM(amount)
  24. FROM children
  25. WHERE parent_id = ids(i))
  26. WHERE id = ids(i);
  27. END AFTER STATEMENT;
  28. END;
  29. /

fiddle

英文:

You would be better to use a VIEW or a MATERIALIZED VIEW to generate the totals.

If you must use a trigger (don't) then you can change from using AFTER ... EACH ROW to an AFTER statement trigger:

  1. CREATE OR REPLACE TRIGGER TRG_TEST
  2. AFTER UPDATE OF AMOUNT ON CHILDREN
  3. BEGIN
  4. UPDATE PARENTS p
  5. SET TOTAL_AMOUNT = (
  6. SELECT SUM(AMOUNT)
  7. FROM CHILDREN c
  8. WHERE c.PARENT_ID = p.ID
  9. );
  10. END;
  11. /

If you want the trigger to be more efficient and only update the ids that are modified then you could use a composite trigger.

  1. CREATE OR REPLACE TRIGGER TRG_TEST
  2. FOR UPDATE OF AMOUNT ON CHILDREN
  3. COMPOUND TRIGGER
  4. TYPE ids_type IS TABLE OF PARENTS.ID%TYPE;
  5. ids ids_type := ids_type();
  6. AFTER EACH ROW
  7. IS
  8. BEGIN
  9. IF :OLD.parent_id IS NOT NULL AND :OLD.parent_id NOT MEMBER OF ids THEN
  10. ids.EXTEND;
  11. ids(ids.COUNT) := :OLD.parent_id;
  12. END IF;
  13. IF :NEW.parent_id IS NOT NULL AND :NEW.parent_id NOT MEMBER OF ids THEN
  14. ids.EXTEND;
  15. ids(ids.COUNT) := :NEW.parent_id;
  16. END IF;
  17. END AFTER EACH ROW;
  18. AFTER STATEMENT
  19. IS
  20. BEGIN
  21. FORALL i IN 1 .. ids.COUNT
  22. UPDATE parents
  23. SET total_amount = (SELECT SUM(amount)
  24. FROM children
  25. WHERE parent_id = ids(i))
  26. WHERE id = ids(i);
  27. END AFTER STATEMENT;
  28. END;
  29. /

fiddle

huangapple
  • 本文由 发表于 2023年5月25日 01:39:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76326134.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定