1

我正在学习在 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。当我SELECT count(*) FROM adresse;在 PL/pgSQL 之外运行时,我得到 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;- 块内。

根据PostgreSQL 文档,如果不在这样的块内,每次都会自动提交插入,因此在提交adresse第一条INSERT语句时不应该有条目。

谁能指出我的错误?

- 编辑:

触发器DEFERRABLE INITIALLY DEFERRED似乎工作正常。我的错误是假设由于我没有使用BEGIN--BlockCOMMIT每个插入都将在自己的事务中执行,并且每次都在之后执行触发器。

然而,即使没有BEGIN-COMMIT所有插入都会被捆绑到一个事务中,然后触发器会被执行。鉴于这种行为,使用BEGIN-有什么意义COMMIT

4

2 回答 2

1

由于鸡和蛋的问题,您需要一个交易加上“DEFERRABLE INITIALLY DEFERRED”。


从两个空表开始:

  • 您不能在 person 表中插入一行,因为它至少需要一个地址。
  • 您不能在地址表中插入一行,因为 FK 约束需要在人员表上存在相应的行

这就是为什么您需要将两个插入捆绑到一个操作中:事务。您需要 BEGIN+ COMMIT,并且 DEFERRABLE 允许存在暂时的禁止数据库状态:它会导致在提交时评估检查。

于 2013-09-01T10:19:44.167 回答
0

这可能看起来有点傻,但答案是您需要停止延迟触发器并BEFORE在插入时运行它。如果在插入后运行,表中当然有数据。

据我所知,这是按预期工作的。

另请注意,您可能不是指:

RAISE EXCEPTION 'No Exception';

你可能想要

RAISE INFO 'No Exception';

然后,您可以更改您的设置并在事务中运行查询,以测试触发器是否执行您希望它执行的操作。事实上,每次插入都会失败,如果不编辑程序,您就无法将其投入生产。

于 2013-09-01T03:46:54.107 回答