我正在学习在 PostgreSQL 中使用触发器,但遇到此代码的问题:
CREATE OR REPLACE FUNCTION checkAdressen() RETURNS TRIGGER AS $$
DECLARE
adrCnt int = 0;
BEGIN
SELECT INTO adrCnt count(*) FROM Adresse
WHERE gehoert_zu = NEW.kundenId;
IF adrCnt < 1 OR adrCnt > 3 THEN
RAISE EXCEPTION 'Customer must have 1 to 3 addresses.';
ELSE
RAISE EXCEPTION 'No exception';
END IF;
END;
$$ LANGUAGE plpgsql;
我在新创建了所有表之后使用此过程创建了一个触发器,因此它们都是空的。然而,上面代码中的 count(*)
函数返回 1。
当我在 PL/pgSQL 之外运行 SELECT count(*) FROM adresse;
时,我得到 0。
我尝试使用 FOUND
变量,但它始终为真。
更奇怪的是,当我将一些值插入到我的表中然后再次删除它们以便它们再次为空时,代码按预期工作并且 count(*)
返回 0。
此外,如果我遗漏了 WHERE gehoert_zu = NEW.kundenId
,count(*)
将返回 0,这意味着我可以使用 WHERE
子句获得更多结果比没有。
--编辑:
这是我如何使用该过程的示例:
CREATE TABLE kunde (
kundenId int PRIMARY KEY
);
CREATE TABLE adresse (
id int PRIMARY KEY,
gehoert_zu int REFERENCES kunde
);
CREATE CONSTRAINT TRIGGER adressenKonsistenzTrigger AFTER INSERT ON Kunde
DEFERRABLE INITIALLY DEFERRED
FOR EACH ROW
EXECUTE PROCEDURE checkAdressen();
INSERT INTO kunde VALUES (1);
INSERT INTO adresse VALUES (1,1);
看起来我在 DEFERRABLE INITIALLY DEFERRED
部分弄错了。我假设触发器将在第一个 INSERT
语句之后执行,但它发生在第二个语句之后,尽管插入不在 BEGIN;
- COMMIT;
- block 。
根据PostgreSQL Documentation如果不在这样的 block 内,每次都会自动提交插入,因此当提交第一个 INSERT
语句时,adresse
中不应有条目。
谁能指出我的错误?
--编辑:
触发器和 DEFERRABLE INITIALLY DEFERRED
似乎工作正常。
我的错误是假设因为我没有使用 BEGIN
-COMMIT
-Block,所以每次插入都将在自己的事务中执行,每次都会在之后执行触发器。
然而,即使没有 BEGIN
-COMMIT
,所有插入也会被捆绑到一个事务中,触发器会在之后执行。
鉴于这种行为,使用 BEGIN
-COMMIT
有什么意义?
最佳答案
由于先有鸡还是先有蛋的问题,您需要一个交易加上“DEFERRABLE INITIALLY DEFERRED”。
从两个空表开始:
- 您不能向人员表中插入一行,因为它至少需要一个地址。
- 不能在 address 表中插入一行,因为 FK 约束需要 person 表上存在相应的行
这就是为什么您需要将两个插入捆绑到一个操作中:事务。您需要 BEGIN+ COMMIT,并且 DEFERRABLE 允许 transient 禁止的数据库状态存在:它导致在提交时评估检查。
关于postgresql - PostgreSQL 中的 PL/pgSQL 查询返回新的空表的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18516816/