postgresql - PostgreSQL 中的递归 BEFORE UPDATE 触发器不使用最后返回的新

标签 postgresql triggers

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

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. 为什么 status = "1"?为什么不是“2”?

最佳答案

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

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

我建议正在发生的事情:

  1. 触发器发出更新 #2。此时该行仍具有其初始 (1,1,1) 值(value)。

  2. 触发器再次启动更新 #2,但它只返回 NEW,因此它不会更改任何内容。

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

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

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

关于postgresql - PostgreSQL 中的递归 BEFORE UPDATE 触发器不使用最后返回的新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21606447/

相关文章:

php - cakephp查询日语单词

java - 在主键列上加入 JPA OneToOne 关系

postgresql - postgres - 什么可能是触发器的依赖对象

mysql - 无法调用 mysql 触发器中的函数。错误是: Undeclared variable

sql - Oracle 11g复合触发器的应用

wpf - Style.Trigger 上的自定义 DependencyProperty

postgresql - 用于在 PostgreSQL 中对 JSONB 搜索结果进行排名的索引

sql - 前 12 个月间隔和 ISO 周的平均值

sql - 检测 postgres 更新触发器中的列更改

MySQL 更新后触发增加计数