计数(*)无法正常工作

Count(*) not working properly

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

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');

您需要执行 AFTER 触发器,而不是 BEFORE 触发器。执行 count(*) "BEFORE" 插入会导致零行,因为尚未插入数据。

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

还有人提到的那些 "unforeseen consequences"?其中之一是你的计数总是 returns 0。所以删除 pragma 既因为它被误用,所以计数将 return 一个正确的值。

另一件事是在触发器中没有提交或回滚。引发错误并让控制代码执行它需要执行的操作。我知道回滚是因为 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;

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

当触发器 必须 访问目标 table 时,最好的选择是将 table 隐藏在视图后面并编写一个 "instead of"在视图上触发。 那个触发器可以访问它想要的table。