Oracle:ON DELETE CASCADE导致触发器递归

时间:2016-11-01 12:59:11

标签: sql oracle triggers cascade

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

每个产品都通过外键 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不喜欢它。 在每个产品的DELETE我得到错误:

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;

1 个答案:

答案 0 :(得分:2)

令人惊讶的是,在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的删除,并且避免无限循环。 / p>