我创建了触发器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');
答案 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"由于数据尚未插入,因此插入的结果为零行。