[英]PL/pgSQL query in PostgreSQL returns result for new, empty table
I am learning to use triggers in PostgreSQL but run into an issue with this code: 我正在学习在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;
I create a trigger with this procedure after freshly creating all my tables so they are all empty. 在重新创建所有表之后,使用此过程创建触发器,使它们全部为空。 However the
count(*)
function in the above code returns 1. When I run SELECT count(*) FROM adresse;
但是上面代码中的
count(*)
函数返回1。当我运行SELECT count(*) FROM adresse;
outside of PL/pgSQL, I get 0. I tried using the FOUND
variable but it is always true. 在PL / pgSQL之外,我得到0。我尝试使用
FOUND
变量,但这始终是正确的。
Even more strangely, when I insert some values into my tables and then delete them again so that they are empty again, the code works as intended and count(*)
returns 0. Also if I leave out the WHERE gehoert_zu = NEW.kundenId
, count(*)
returns 0 which means I get more results with the WHERE
clause than without. 更奇怪的是,当我在表中插入一些值然后再次删除它们以便它们再次为空时,代码将按预期工作,并且
count(*)
返回0。同样,如果我忽略了WHERE gehoert_zu = NEW.kundenId
, count(*)
返回0,这意味着使用WHERE
子句可以获得比不使用更多的结果。
--Edit: - 编辑:
Here is an example of how I use the procedure: 这是我如何使用该过程的示例:
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);
It looks like I am getting the DEFERRABLE INITIALLY DEFERRED
part wrong. 看来我将
DEFERRABLE INITIALLY DEFERRED
部分弄错了。 I assumed the trigger would be executed after the first INSERT
statement but it happens after the second one, although the inserts are not inside a BEGIN;
我假设触发器将在第一个
INSERT
语句之后执行,但它会在第二个INSERT
语句之后执行,尽管插入内容不在BEGIN;
- COMMIT;
-
COMMIT;
- Block. -块
According to the PostgreSQL Documentation inserts are commited automatically every time if not inside such a block and thus there shouldn't be an entry in adresse
when the first INSERT
statement is commited. 根据PostgreSQL文档刀片自动每次COMMITED如果没有这样一个块内,因此不应该有一个条目中的
adresse
当第一INSERT
语句将提交。
Can anyone point out my mistake? 谁能指出我的错误?
--Edit: - 编辑:
The trigger and DEFERRABLE INITIALLY DEFERRED
seem to be working all right. 触发器和
DEFERRABLE INITIALLY DEFERRED
似乎工作正常。 My mistake was to assume that since I am not using a BEGIN
- COMMIT
-Block each insert would be executed in an own transaction with the trigger being executed afterwards every time. 我的错误是假设由于我不使用
BEGIN
- COMMIT
-Block,所以每个插入操作都将在自己的事务中执行,而触发器每次之后都将执行。
However even without the BEGIN
- COMMIT
all inserts get bundled into one transaction and the trigger is executed afterwards. 但是,即使没有
BEGIN
- COMMIT
所有插入也都捆绑到一个事务中,然后执行触发器。 Given this behaviour, what is the point in using BEGIN
- COMMIT
? 鉴于这种行为,使用
BEGIN
- COMMIT
什么意义?
You need a transaction plus the "DEFERRABLE INITIALLY DEFERRED" because of the chicken and egg problem. 由于鸡肉和鸡蛋的问题,您需要进行一笔交易,并加上“可延期的初始延期”。
starting with two empty tables: 从两个空表开始:
This is why you need to bundle the two inserts into one operation: the transaction. 这就是为什么您需要将两个插入内容捆绑到一个操作中:事务。 You need the BEGIN+ COMMIT, and the DEFERRABLE allows transient forbidden database states to exists: it causes the check to be evaluated at commit time.
您需要BEGIN + COMMIT,并且DEFERRABLE允许存在暂时禁止的数据库状态 :这将导致在提交时评估检查。
This may seem a bit silly, but the answer is you need to stop deferring the trigger and run it BEFORE
the insert. 这似乎有点愚蠢,但是答案是您需要停止延迟触发器,并在插入
BEFORE
运行它。 If you run it after the insert, of course there is data in the table. 如果您在插入后运行它,那么表中当然会有数据。
As far as I can tell this is working as expected. 据我所知,这按预期工作。
One further note, you probably dont mean: 进一步说明,您可能不是要说的:
RAISE EXCEPTION 'No Exception';
You probably want 你可能想要
RAISE INFO 'No Exception';
Then you can change your settings and run queries in transactions to test that the trigger does what you want it to do. 然后,您可以更改设置并在事务中运行查询以测试触发器是否按照您希望的方式进行。 As it is, every insert is going to fail and you have no way to move this into production without editing your procedure.
实际上,每个插入都会失败,如果不编辑过程就无法将其移入生产环境。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.