【问题标题】:Oracle: ON DELETE CASCADE causing trigger recursionOracle:ON DELETE CASCADE 导致触发器递归
【发布时间】:2017-03-14 14:54:09
【问题描述】:

我们以这两个表为例:Customer 包含客户,Product 包含客户购买/使用的产品。

每个产品都通过外键 CustomerID 引用一个客户,该外键对应于表 Customer 的主键(它们也具有相同的名称)。

删除客户时,所有引用该客户的产品都将被删除:Product.CustomerID 具有属性ON DELETE CASCADE

现在假设基地的客户至少应该拥有一种产品:
当一个产品被移除时,如果它是一个客户的最后一个产品,那么这个客户也必须被移除。

CREATE OR REPLACE TRIGGER RemoveCustomer
AFTER DELETE ON Product
BEGIN
        DELETE FROM Customer
        WHERE CustomerID IN (
                SELECT c.CustomerID
                FROM Customer c
                LEFT OUTER JOIN Product p
                    ON p.CustomerID = c.CustomerID
                GROUP BY c.CustomerID HAVING COUNT(p.CustomerID) = 0
        );
END;
/

这个解决方案对我来说似乎很自然,但 Oracle 不喜欢它。 每次删除产品时,我都会收到错误消息:

ORA-00036: maximum number of recursive SQL levels (50) exceeded 

这即使 DELETE 不会导致程序被删除。

令人惊讶的是,这种语法工作得很好:

CREATE OR REPLACE TRIGGER RemoveCustomer
AFTER DELETE ON Product
BEGIN
        FOR my_row IN (
                SELECT c.CustomerID
                FROM Customer c
                LEFT OUTER JOIN Product p
                    ON p.CustomerID = c.CustomerID
                GROUP BY c.CustomerID HAVING COUNT(p.CustomerID) = 0
        ) 
        LOOP
                DELETE FROM Customer WHERE CustomerID = my_row.CustomerID;
        END LOOP;

END;
/

有人能解释一下为什么会这样吗?

编辑:

这里有一个工作示例:

CREATE TABLE Customer (
    CustomerID INTEGER                 PRIMARY KEY
);

CREATE TABLE Product (
    ProductID INTEGER                 PRIMARY KEY,
    CustomerID INTEGER,
    CONSTRAINT fk_Customer FOREIGN KEY (CustomerID)
                                        REFERENCES Customer
                                        ON DELETE CASCADE
);


INSERT INTo Customer (CustomerID) VALUES (0);
INSERT INTo Customer (CustomerID) VALUES (1);
INSERT INTo Customer (CustomerID) VALUES (2);
INSERT INTo Customer (CustomerID) VALUES (3);
INSERT INTo Customer (CustomerID) VALUES (4);
INSERT INTo Customer (CustomerID) VALUES (5);
INSERT INTo Customer (CustomerID) VALUES (6);

INSERT INTO Product (ProductID, CustomerID) VALUES (0, 0);
INSERT INTO Product (ProductID, CustomerID) VALUES (1, 0);
INSERT INTO Product (ProductID, CustomerID) VALUES (2, 1);
INSERT INTO Product (ProductID, CustomerID) VALUES (3, 2);
INSERT INTO Product (ProductID, CustomerID) VALUES (4, 3);
INSERT INTO Product (ProductID, CustomerID) VALUES (5, 3);
INSERT INTO Product (ProductID, CustomerID) VALUES (6, 3);
INSERT INTO Product (ProductID, CustomerID) VALUES (7, 4);
INSERT INTO Product (ProductID, CustomerID) VALUES (8, 5);
INSERT INTO Product (ProductID, CustomerID) VALUES (9, 5);
INSERT INTO Product (ProductID, CustomerID) VALUES (10, 6);


CREATE OR REPLACE TRIGGER RemoveCustomer
AFTER DELETE ON Product
BEGIN
        DELETE FROM Customer
        WHERE CustomerID IN (
                SELECT c.CustomerID
                FROM Customer c
                LEFT OUTER JOIN Product p
                    ON p.CustomerID = c.CustomerID
                GROUP BY c.CustomerID HAVING COUNT(p.CustomerID) = 0
        );
END;
/


/* This request will produce the error */
DELETE FROM Product WHERE CustomerID = 3;

【问题讨论】:

    标签: sql oracle triggers cascade


    【解决方案1】:

    令人惊讶,但似乎products 上的级联删除语句总是 在对customers 执行删除之后发生 - 即使没有删除客户。例如:

    SQL> delete customer where customerid = 9999999;
    delete customer where customerid = 9999999
           *
    ERROR at line 1:
    ORA-00036: maximum number of recursive SQL levels (50) exceeded
    ORA-06512: at "TTEST.REMOVECUSTOMER", line 2
    ...
    

    使用您的触发器的第二个版本,for 循环体在没有客户没有产品时永远不会执行,因此customers 的删除永远不会发生,并且可以避免无限循环。

    【讨论】:

    • 使用触发器的第二个版本,即使执行循环也没有递归。这正常吗?
    • @TTK - 循环被执行,但第二次没有找到匹配的行,所以循环内的删除再次执行。
    • @Alex Poole - 如果我是对的,假设只需要删除一个客户:第一次执行循环时,它会对客户执行 DELETE。由于级联删除语句,DELETE 应该再次执行触发器。此时我们有一个递归级别,但这次触发器不会进入循环内部,因为唯一的客户已经被删除。我说的对吗?
    • 托尼,这是一个语句级触发器。每个语句都会被解雇。 where 条件的计算结果是真还是假并不重要
    • @NicholasKrasnov - 无论产品是否被删除,令人惊讶的一点不是触发器触发;即使没有客户被删除,客户(来自产品 FK)的级联删除仍然会进行名义上的产品删除(因此间接触发触发器)?
    猜你喜欢
    • 1970-01-01
    • 2012-01-06
    • 2011-09-21
    • 2014-09-26
    • 2015-01-07
    • 1970-01-01
    • 2015-11-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多