2012-01-02 21 views
1

之前,我将尝试使用触发器来避免在某些情况下在表Products删除一排。代码如下突变表中的触发器

CREATE TRIGGER trgPreventProductRemoval 
    BEFORE DELETE ON Products 
    FOR EACH ROW 

    BEGIN 
     DECLARE 
      l_custid INTEGER; 
     BEGIN 
      SELECT count(*) INTO l_custid FROM Orders WHERE product = :old.prodDescription ; 

      IF l_custid > 0 THEN 
       raise_application_error (-20100, 'You can not delete a product that has active orders!'); 
      END IF; 
     END; 
    END; 

但是我得到的错误:table ORDERS is mutating, trigger/function may not see it

我该如何解决?

编辑-SOLUTION:我已经接受了下面,因为它是“政治正确”的解决方案。由于某些“限制”,我无法使用它并最终找到了不同的解决方法。看到我单独发布的解决方案。

+0

的可能重复的[ORA-04091:表\ [嗒嗒\]被突变,触发/功能可能无法看到它](http://stackoverflow.com/questions/375968/ora-04091-table-blah-is -mutating-trigger-function-may-not-see-it) – Sathya 2012-01-03 04:15:10

回答

5

在订单和产品表之间使用外键代替触发器。

+0

这将是更清洁的解决方案,但我不能这样做。 – niels 2012-01-02 18:34:21

+4

不允许?这似乎有点荒谬。外键的全部目的是为了解决你所面临的问题。我知道你是这里的“受害者”,是一个非常非常糟糕的政策。但是,考虑到你一定会遇到的竞争条件和锁定问题,你可能会反对使用外键的政治斗争,而不是试图对其进行编码。 – 2012-01-02 18:51:01

+0

@Mark J. Bobak:我在做H/W,我必须用触发器来做,因为我被要求这样做。如果我必须选择,我肯定会用fkeys来做。 – niels 2012-01-02 18:57:55

0

不能修复这个错误,因为你从选择的表在DML操作过程中的会话你因此无法确定在查询时对查询的回答是什么。

有,然而,一个稍显凌乱,但简单的方法解决这问题:

  1. 创建形式select * from products的景色。
  2. 在视图上编译触发器而不是表格。
  3. 确保触发器执行你居然打算表的DML操作(凌乱的部分)。
  4. 只对视图而非表格执行DML操作。

所以,这样的事情应该工作。

create or replace view v_products as select * from products; 

CREATE TRIGGER trgPreventProductRemoval 
    BEFORE DELETE ON v_products 
    FOR EACH ROW 

    DECLARE 

    l_custid INTEGER; 

    BEGIN 

     SELECT count(*) INTO l_custid 
     FROM Orders 
     WHERE product = :old.prodDescription; 

     IF l_custid > 0 THEN 
     raise_application_error (-20100, 'You can not delete a product that has active orders!'); 
     END IF; 

     -- assumed, I don-t know the actual columns. 
     delete from products where product = :new.product_id; 

    END trgPreventProductRemoval; 
+0

@niels那会是因为我忘了添加一个'declare' - 我已经编辑了答案;我从语法中推断出Oracle,这都是Oracle,所以不用担心。但是,在这个特例中,@ shannon的解决方案实际上比我的这个小小的破解要好得多。 – Ben 2012-01-02 19:32:26

+0

这不起作用,因为Oracle不允许在视图上使用“之前”触发器。这并不重要,因为我找到了一种适用于我正在使用的特定数据库的解决方法(请参阅我的发布解决方案)。谢谢你的关心。 – niels 2012-01-02 19:43:44

0

问题是最后,除去ProductsOrders之间的“链接”来解决。该链接是一个名为SuplProd的中间表的外键中的ON DELETE CASCADE。通过从SuplProd的fk中去除ON DELETE CASCADE,表Orders变为非变异。

要利用用于去除ON DELETE CASCADE,我刚添加的触发器的代码一行代码,只是END IF;

后因此,从技术上说,这种解决方法的工作原理进行必要的表中的非变异的。