PostgreSQL中的PL / pgSQL查询返回新的空表的结果

时间:2013-08-29 16:56:33

标签: postgresql plpgsql

我正在学习在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.kundenIdcount(*)会返回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; - adresse - Block中。

根据PostgreSQL Documentation插入,如果不在这样的块内,每次都会自动提交,因此当提交第一个INSERT语句时,DEFERRABLE INITIALLY DEFERRED中不应该有一个条目。

任何人都可以指出我的错误吗?

- 编辑:

触发器和BEGIN似乎正常工作。 我的错误是假设因为我没有使用COMMIT - BEGIN - 阻止每个插入都将在自己的事务中执行,并且每次都会执行触发器。

然而,即使没有COMMIT - BEGIN,所有插入都会捆绑到一个事务中,之后会执行触发器。 鉴于此行为,使用COMMIT - {{1}}?

有什么意义

2 个答案:

答案 0 :(得分:1)

由于鸡肉和鸡蛋问题,您需要一个交易加上“可延迟初始延期”。


从两个空表开始:

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

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

答案 1 :(得分:0)

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

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

还有一点需要注意,你可能并不是说:

RAISE EXCEPTION 'No Exception';

你可能想要

RAISE INFO 'No Exception';

然后,您可以更改设置并在事务中运行查询,以测试触发器是否符合您的要求。实际上,每次插入都会失败,如果不编辑程序,就无法将其移植到生产环境中。