计数(*)不能正常工作

时间:2015-04-10 15:14:49

标签: sql oracle plsql triggers count

我创建了触发器A1,以便具有特定类型的文章,即Bert'不能添加超过一次,它只能在库存中只有1。 但是,虽然我创建了触发器,但我仍然可以添加一个类型为“Bert”的文章。不知何故,计数返回' 0' 0但是当我运行相同的sql语句时,它返回正确的数字。如果我放下触发器并重新添加它,它也会正确开始计数。什么想法可能会出错?

TRIGGER A1 BEFORE INSERT ON mytable
FOR EACH ROW
DECLARE
 l_count NUMBER;
 PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  SELECT COUNT(*) INTO l_count FROM mytable WHERE article = :new.article;

    dbms_output.put_line('Count: ' || l_count);
  IF l_count >0  THEN
    IF(:new.TYPEB = 'Bert') THEN 
      dbms_output.put_line('article already exists!');
      ROLLBACK; 
    END IF;
  ELSIF (:new.TYPEB = 'Bert' AND :new.stock_count>1) THEN
    dbms_output.put_line('stock cannot have more than 1 of this article with type Bert');
    ROLLBACK; 
  END IF;
END;

这是我使用的插入语句:

INSERT INTO mytable VALUES('Chip',1,9,1,'Bert');

2 个答案:

答案 0 :(得分:1)

有几点。首先,您滥用自治事务编译指示。它适用于您需要独立于主事务提交或回滚的单独事务。您正在使用它来回滚主事务 - 如果没有错误,您永远不会提交。

有人提到的那些“不可预见的后果”?其中一个是你的计数总是返回0.所以删除pragma,因为它被误用,所以计数将返回一个正确的值。

另一件事是在触发器中没有提交或回滚。提出错误并让控制代码执行它需要做的事情。我知道回滚是因为pragma。删除编译指示时,不要忘记删除它们。

以下触发器对我有用:

CREATE OR REPLACE TRIGGER trg_mytable_biu 
BEFORE INSERT OR UPDATE ON mytable 
FOR EACH ROW 
WHEN (NEW.TYPEB = 'Bert') -- Don't even execute unless this is Bert
DECLARE
    L_COUNT NUMBER;
BEGIN
    SELECT  COUNT(*) INTO L_COUNT
    FROM    MYTABLE 
    WHERE   ARTICLE = :NEW.ARTICLE
        AND TYPEB = :NEW.TYPEB;

    IF L_COUNT > 0  THEN
        RAISE_APPLICATION_ERROR( -20001, 'Bert already exists!' );
    ELSIF :NEW.STOCK_COUNT > 1 THEN
        RAISE_APPLICATION_ERROR( -20001, 'Can''t insert more than one Bert!' );
    END IF;
END;

但是,对表上的触发器单独访问该表并不是一个好主意。通常系统甚至不会允许它 - 如果更改为“之后”,则此触发器根本不会执行。如果允许执行,则无法确定获得的结果 - 正如您已经发现的那样。实际上,我对上面的触发器有点惊讶。在真实的数据库中使用它会让我感到不安。

当触发器必须访问目标表时,最好的选择是隐藏视图后面的表并在视图上写一个“而不是”触发器。 那个触发器可以随心所欲地访问该表。

答案 1 :(得分:0)

你需要做一个AFTER触发器,而不是BEFORE触发器。做计数(*)" BEFORE"由于数据尚未插入,因此插入的结果为零行。