【问题标题】:Recursive BEFORE UPDATE trigger in PostgreSQL don't use last returned newPostgreSQL 中的递归 BEFORE UPDATE 触发器不使用最后返回的新
【发布时间】:2014-02-06 15:04:16
【问题描述】:

我有一个带有更新前触发器的表,它可以通过更新同一行来触发自己,但更新语句的结果不是该触发器返回的最后一个值。我不明白它为什么以及如何工作。这个问题在例子上更容易理解:

BEGIN;

SET client_min_messages = 'notice';

CREATE TABLE public.test (
  id INTEGER, 
  status INTEGER, 
  value numeric
);

CREATE SEQUENCE test_seq;

CREATE OR REPLACE FUNCTION public.test_tr() RETURNS trigger AS $body$
DECLARE
    seq integer;
BEGIN
    seq := nextval('test_seq');
    RAISE NOTICE 'Started: %', seq;
    IF new.status != old.status THEN
        UPDATE test SET value = new.value WHERE id = new.id;
    END IF;
    RAISE NOTICE 'Finished: %, RETURNED NEW: %', seq, new;
    RETURN new;
END
$body$ LANGUAGE 'plpgsql';

CREATE TRIGGER test_tr BEFORE UPDATE ON test FOR EACH ROW EXECUTE PROCEDURE test_tr();

INSERT INTO test (id, status, value) VALUES (1, 1, 1);

UPDATE test SET status = 2, value = 2 WHERE id = 1;

SELECT * FROM test;

ROLLBACK;

产生如下:

NOTICE:  Started: 1
NOTICE:  Started: 2
NOTICE:  Finished: 2, RETURNED NEW: (1,1,2)
NOTICE:  Finished: 1, RETURNED NEW: (1,2,2)
UPDATE 0

 id | status | value 
----+--------+-------
  1 |      1 |     2

如您所见,最后返回的 new 是 (1,2,2) (status = 2, value = 2),但最后一个 SELECT 返回的行“status” = 1 和“value " = 2。为什么状态 = "1"?为什么不是“2”?

【问题讨论】:

  • 您应该使用语句级触发器,而不是行级触发器。

标签: postgresql triggers


【解决方案1】:

BEFORE UPDATE 触发器在同一行调用UPDATE 时,该手册似乎没有定义任何特定行为。

直觉上,我们可能期望NEW 的最后一个值进入该行,但结果表明,事实并非如此。

我的建议正在发生:

  1. 触发器发出 UPDATE #2。此时该行仍有其初始(1,1,1) 价值。

  2. 触发器为 UPDATE #2 再次启动,但它只返回 NEW,所以它不会改变任何东西。

  3. 一旦 UPDATE #2 完成,该行的新版本就会变为当前版本,内容为 (1,1,2)

  4. 当 UPDATE #1 完成时,引擎应该写入 NEW=(1,2,2),但随后它注意到它正在处理的行的版本已经被取代。所以它会丢弃它的NEW 记录并保留它。

这与 psql 报告 UPDATE 0 一致:UPDATE #1 实际上没有更新任何行。

【讨论】:

    猜你喜欢
    • 2021-11-03
    • 2018-05-16
    • 2013-01-07
    • 2010-10-17
    • 1970-01-01
    • 2013-01-24
    • 2021-11-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多