计数(*)无法正常工作
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。
我创建了触发器 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。