PostgreSQL 触发器中的合并不会在更新时触发

Coalesce in PostgreSQL trigger does not fire on update

在古生物学数据库的上下文中 collection,我在 PostgreSQL v.12 中有这个 table 定义:

CREATE TABLE taxon (
id                  integer  DEFAULT NEXTVAL('taxon_oid_seq')   PRIMARY KEY,
taxon                varchar(100)         UNIQUE  NOT NULL,
reino                varchar(50)                  NOT NULL,
phylum               varchar(100)         ,
subphylum            varchar(100)         ,
classe               varchar(100)         ,
subclasse            varchar(100)         ,
superordem           varchar(100)         ,
ordem                varchar(100)         ,
subordem             varchar(100)         ,
infraordem           varchar(100)         ,
familia              varchar(100)         ,
subfamilia           varchar(100)         ,
genero               varchar(100)         ,
especie              varchar(100)         ,
subespecie           varchar(100)             );

分类单元字段将自动填充可以确定给定物种分类的最低级别。为了实现这一点,我有这个触发器:

CREATE OR REPLACE FUNCTION get_taxon() RETURNS TRIGGER LANGUAGE
plpgsql AS $BODY$ 
BEGIN   
NEW.taxon := coalesce(NEW.subespecie, NEW.especie, NEW.genero, NEW.subfamilia, 
                      NEW.familia, NEW.infraordem, NEW.subordem, NEW.ordem, NEW.superordem,
                      NEW.subclasse, NEW.classe, NEW.subphylum, NEW.phylum, NEW.reino);   
RETURN NEW; 
END; 
$BODY$ 
VOLATILE; 

CREATE TRIGGER update_taxon
BEFORE INSERT OR UPDATE ON taxon
FOR EACH ROW EXECUTE PROCEDURE get_taxon();

然而,此触发器仅在 INSERT 时触发,如果创建 UPDATE 则不会发生任何事情。如何在 UPDATE 的情况下也触发此触发器?

编辑

我刚刚意识到我有另一个在更新时触发的触发器。此其他触发器定义为:

CREATE OR REPLACE FUNCTION taxon_history_update() RETURNS trigger AS 
    $BODY$
    BEGIN
            INSERT INTO history.taxon(operacao, data, tecnico, original_oid, 
                                      taxon, reino, phylum, subphylum, classe, 
                                      subclasse, superordem, ordem, subordem, 
                                      infraordem, familia, subfamilia, genero, 
                                      especie, subespecie) 
                                VALUES        
                                      ('UPDATE', current_timestamp, current_user, 
                                       old.oid, old.taxon, old.reino, old.phylum, 
                                       old.subphylum, old.classe, old.subclasse, 
                                       old.superordem, old.ordem, old.subordem, 
                                       old.infraordem, old.familia, 
                                       old.subfamilia, old.genero, old.especie, 
                                       old.subespecie);
                                       RETURN old;
    END;
    $BODY$
    LANGUAGE plpgsql;

CREATE TRIGGER taxon_history_update
BEFORE UPDATE ON taxon
FOR EACH ROW EXECUTE PROCEDURE taxon_history_update();

这两个触发器之间会不会存在某种conflict/interferance?如果我删除 taxon_history_update 触发器,我的 update_taxon 触发器也适用于更新!

我正在写一个答案,因为我的评论不适合:

如果我创建您的 table 和触发器,然后执行 INSERTUPDATE,我得到:

foo=# select * from taxon;
 id | taxon | reino | phylum | subphylum | classe | subclasse | superordem | ordem | subordem | infraordem | familia | subfamilia | genero | especie | subespecie 
----+-------+-------+--------+-----------+--------+-----------+------------+-------+----------+------------+---------+------------+--------+---------+------------
(0 rows)

foo=# explain analyze insert into taxon (reino) values ('sapienz');
                                          QUERY PLAN                                           
-----------------------------------------------------------------------------------------------
 Insert on taxon  (cost=0.00..0.01 rows=1 width=495) (actual time=0.111..0.111 rows=0 loops=1)
   ->  Result  (cost=0.00..0.01 rows=1 width=495) (actual time=0.062..0.063 rows=1 loops=1)
 Planning Time: 0.034 ms
 Trigger update_taxon: time=0.026 calls=1
 Execution Time: 0.137 ms
(5 rows)

foo=# select * from taxon;
 id |  taxon  |  reino  | phylum | subphylum | classe | subclasse | superordem | ordem | subordem | infraordem | familia | subfamilia | genero | especie | subespecie 
----+---------+---------+--------+-----------+--------+-----------+------------+-------+----------+------------+---------+------------+--------+---------+------------
  1 | sapienz | sapienz |        |           |        |           |            |       |          |            |         |            |        |         | 
(1 row)

foo=# explain analyze update taxon set reino = 'sapien' where id = 1;
                                                        QUERY PLAN                                                         
---------------------------------------------------------------------------------------------------------------------------
 Update on taxon  (cost=0.14..8.16 rows=1 width=1005) (actual time=0.134..0.135 rows=0 loops=1)
   ->  Index Scan using taxon_pkey on taxon  (cost=0.14..8.16 rows=1 width=1005) (actual time=0.017..0.019 rows=1 loops=1)
         Index Cond: (id = 1)
 Planning Time: 0.100 ms
 Trigger update_taxon: time=0.039 calls=1
 Execution Time: 0.162 ms
(6 rows)

foo=# select * from taxon;
 id | taxon  | reino  | phylum | subphylum | classe | subclasse | superordem | ordem | subordem | infraordem | familia | subfamilia | genero | especie | subespecie 
----+--------+--------+--------+-----------+--------+-----------+------------+-------+----------+------------+---------+------------+--------+---------+------------
  1 | sapien | sapien |        |           |        |           |            |       |          |            |         |            |        |         | 
(1 row)

如您所见,触发器在两个 EXPLAIN ANALYZE 调用中都被触发了——您能否更新您的问题,提供有关如何重现您看到的行为的更多详细信息?顺便说一句,我正在使用 PostgreSQL v. 12.0

UPDATE 根据您的更新: 啊,我认为你的问题是你正在为第二个触发器做 RETURN old;——你需要 RETURN new 否则它会丢弃更改。根据 the documentation:

A row-level BEFORE trigger that does not intend to cause either of these behaviors must be careful to return as its result the same row that was passed in (that is, the NEW row for INSERT and UPDATE triggers, the OLD row for DELETE triggers).

我想,您的历史记录触发函数中有错字。在 table 分类单元中没有 "oid" 列,但 "id" 列(因此记录变量 "old")。这会中断触发函数的执行,并可能回滚事务。

顺便说一句:自 PostgreSQL v12 以来,所有 table 都缺少内部 "oid" 列,如您在文档中所见(CREATE TABLE:“...创建一个 table WITH OIDS 不再受支持。”),即使用 "oid" 可能一直有效到第 11 页。

希望对您有所帮助。

你的问题是第二个触发器。这是一个 BEFORE UPDATE 触发器 returns old 而不是 new 它应该是

这意味着原始行 (old) 将被写入 table 而不是更新后的行 (new)。所以你的更新不会做任何事情。

由于 BEFORE UPDATE 触发器按字母顺序执行,因此 update_taxon 触发器将获取旧行而不是新行,如 new.

taxon_history_update功能改成RETURN new;,你的烦恼就没了