2024 年 9 月 26 日:PostgreSQL 17 发布!
支持版本:当前 (17) / 16 / 15 / 14 / 13 / 12
开发版本:devel
不支持的版本:11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

CREATE TRIGGER

CREATE TRIGGER — 定义一个新的触发器

语法

CREATE [ OR REPLACE ] [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }
    ON table_name
    [ FROM referenced_table_name ]
    [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ]
    [ REFERENCING { { OLD | NEW } TABLE [ AS ] transition_relation_name } [ ... ] ]
    [ FOR [ EACH ] { ROW | STATEMENT } ]
    [ WHEN ( condition ) ]
    EXECUTE { FUNCTION | PROCEDURE } function_name ( arguments )

where event can be one of:

    INSERT
    UPDATE [ OF column_name [, ... ] ]
    DELETE
    TRUNCATE

描述

CREATE TRIGGER 创建一个新的触发器。 CREATE OR REPLACE TRIGGER 将创建新的触发器,或替换现有触发器。触发器将与指定的表、视图或外部表关联,并在对该表执行某些操作时执行指定的函数 function_name

要替换现有触发器的当前定义,请使用 CREATE OR REPLACE TRIGGER,指定现有触发器的名称和父表。所有其他属性都将被替换。

可以将触发器指定为在尝试对行执行操作之前触发(在检查约束并尝试执行 INSERTUPDATEDELETE 之前);或在操作完成后触发(在检查约束并完成执行 INSERTUPDATEDELETE 之后);或者代替操作触发(在对视图进行插入、更新或删除的情况下)。如果触发器在事件之前或代替事件触发,则触发器可以跳过对当前行的操作,或更改正在插入的行(仅适用于 INSERTUPDATE 操作)。如果触发器在事件之后触发,则所有更改(包括其他触发器的效果)都对触发器 可见

标记为 FOR EACH ROW 的触发器将针对操作修改的每一行调用一次。例如,影响 10 行的 DELETE 将导致目标关系上的任何 ON DELETE 触发器被分别调用 10 次,每次针对一个删除的行。相反,标记为 FOR EACH STATEMENT 的触发器仅针对任何给定的操作执行一次,而无论它修改了多少行(特别是,修改零行的操作仍然会导致执行任何适用的 FOR EACH STATEMENT 触发器)。

指定为在 INSTEAD OF 触发事件触发的触发器必须标记为 FOR EACH ROW,并且只能在视图上定义。视图上的 BEFOREAFTER 触发器必须标记为 FOR EACH STATEMENT

此外,可以定义触发器,使其针对 TRUNCATE 触发,但只能针对 FOR EACH STATEMENT

下表总结了哪些类型的触发器可用于表、视图和外部表

何时 事件 行级 语句级
BEFORE INSERT/UPDATE/DELETE 表和外部表 表、视图和外部表
TRUNCATE 表和外部表
AFTER INSERT/UPDATE/DELETE 表和外部表 表、视图和外部表
TRUNCATE 表和外部表
INSTEAD OF INSERT/UPDATE/DELETE 视图
TRUNCATE

此外,触发器定义可以指定一个布尔型 WHEN 条件,该条件将被测试以查看是否应触发触发器。在行级触发器中,WHEN 条件可以检查行的列的旧值和/或新值。语句级触发器也可以有 WHEN 条件,尽管该功能对它们来说不是那么有用,因为该条件不能引用表中的任何值。

如果为同一事件定义了多个相同类型的触发器,则它们将按名称的字母顺序触发。

当指定了 CONSTRAINT 选项时,此命令将创建一个 约束触发器 这与普通触发器相同,只是触发器触发的时机可以使用 SET CONSTRAINTS 进行调整。约束触发器必须是普通表(而不是外部表)上的 AFTER ROW 触发器。它们可以在导致触发事件的语句结束时触发,或者在包含事务的结束时触发;在后一种情况下,它们被称为 延迟。可以通过使用 SET CONSTRAINTS 来强制立即发生挂起的延迟触发器触发。

REFERENCING 选项被指定时,它能够收集 转换关系,这些关系是包括当前 SQL 语句插入、删除或修改的所有行的行集。此功能允许触发器查看语句所做的全局视图,而不仅仅是一次查看一行。此选项仅适用于非约束触发器的 AFTER 触发器;此外,如果触发器是 UPDATE 触发器,则它不得指定 column_name 列表。 OLD TABLE 只能指定一次,并且只能用于可以在 UPDATEDELETE 上触发的触发器;它创建一个包含语句更新或删除的所有行的 前映像的转换关系。类似地, NEW TABLE 只能指定一次,并且只能用于可以在 UPDATEINSERT 上触发的触发器;它创建一个包含语句更新或插入的所有行的 后映像的转换关系。

SELECT 不修改任何行,因此您无法创建 SELECT 触发器。规则和视图可以为似乎需要 SELECT 触发器的问题提供可行的解决方案。

有关触发器的更多信息,请参阅 第 37 章

参数

name

要为新的触发器指定的名称。这必须与同一表的任何其他触发器的名称不同。名称不能是模式限定的——触发器将继承其表的模式。对于约束触发器,这也是使用 SET CONSTRAINTS 修改触发器行为时使用的名称。

BEFORE
AFTER
INSTEAD OF

确定在事件之前、之后还是代替事件调用函数。约束触发器只能指定为 AFTER

event

INSERTUPDATEDELETETRUNCATE 之一;这指定了将触发触发器的事件。可以使用 OR 指定多个事件,但在请求转换关系时除外。

对于 UPDATE 事件,可以使用此语法指定一个列列表

UPDATE OF column_name1 [, column_name2 ... ]

只有当列出的列中至少有一个列被提及为 UPDATE 命令的目标,或者列出的列中有一个依赖于作为 UPDATE 目标的列的生成列时,触发器才会触发。

INSTEAD OF UPDATE 事件不允许列列表。请求转换关系时也不能指定列列表。

table_name

触发器所针对的表、视图或外部表的名称(可选模式限定)。

referenced_table_name

约束引用的另一个表的(可能是模式限定的)名称。此选项用于外键约束,不建议用于一般用途。这只能用于约束触发器。

DEFERRABLE
NOT DEFERRABLE
INITIALLY IMMEDIATE
INITIALLY DEFERRED

触发器的默认时机。有关这些约束选项的详细信息,请参阅 CREATE TABLE 文档。这只能用于约束触发器。

REFERENCING

此关键字紧接在提供对触发语句的转换关系的访问权限的一个或两个关系名称的声明之前。

OLD TABLE
NEW TABLE

此子句指示以下关系名称是用于前映像转换关系还是后映像转换关系。

transition_relation_name

在触发器内用于此转换关系的(非限定)名称。

FOR EACH ROW
FOR EACH STATEMENT

这指定了触发函数是否应该为触发事件影响的每一行触发一次,或者只为每个 SQL 语句触发一次。 如果没有指定,FOR EACH STATEMENT 是默认值。 约束触发器只能指定为 FOR EACH ROW

条件

一个布尔表达式,用于确定触发函数是否会被实际执行。 如果指定了 WHEN,则只有当 condition 返回 true 时才会调用该函数。 在 FOR EACH ROW 触发器中,WHEN 条件可以通过分别写入 OLD.column_nameNEW.column_name 来引用旧行或新行值的列。 当然,INSERT 触发器不能引用 OLD,而 DELETE 触发器不能引用 NEW

INSTEAD OF 触发器不支持 WHEN 条件。

目前,WHEN 表达式不能包含子查询。

请注意,对于约束触发器,WHEN 条件的评估不会延迟,而是在执行行更新操作后立即发生。 如果条件没有评估为 true,则不会将触发器排队以进行延迟执行。

函数名

一个用户提供的函数,声明为不接受任何参数并返回类型 trigger,在触发器触发时执行。

CREATE TRIGGER 语法中,关键字 FUNCTIONPROCEDURE 等效,但引用的函数在任何情况下都必须是函数,而不是过程。 在这里使用关键字 PROCEDURE 是历史性的,并且已弃用。

参数

可选的逗号分隔的参数列表,在触发器执行时提供给该函数。 参数是文字字符串常量。 也可以在这里写简单的名称和数字常量,但它们都将被转换为字符串。 请查看触发函数实现语言的描述,以了解如何在函数内部访问这些参数; 它可能不同于正常的函数参数。

注意

要在一个表上创建或替换一个触发器,用户必须对该表具有 TRIGGER 权限。 用户还必须对触发函数具有 EXECUTE 权限。

使用 DROP TRIGGER 删除触发器。

在一个分区表上创建一个行级触发器将导致在每个现有的分区上创建一个相同的 克隆 触发器; 以后创建或附加的任何分区也将有一个相同的触发器。 如果子分区上已经有名称冲突的触发器,则会发生错误,除非使用 CREATE OR REPLACE TRIGGER,在这种情况下,该触发器将被替换为克隆触发器。 当一个分区从其父分区分离时,其克隆触发器将被删除。

一个特定于列的触发器(使用 UPDATE OF column_name 语法定义的)将在 UPDATE 命令的 SET 列表中列出其任何列作为目标时触发。 列的值可能在触发器未触发时发生变化,因为 BEFORE UPDATE 触发器对行内容所做的更改不被认为是更改。 相反,像 UPDATE ... SET x = x ... 这样的命令会触发列 x 上的触发器,即使列的值没有改变。

在一个 BEFORE 触发器中,WHEN 条件在函数执行或将要执行之前进行评估,因此使用 WHEN 与在触发函数开头测试相同的条件在本质上没有区别。 特别要注意,条件所看到的 NEW 行是当前值,可能被先前的触发器修改过。 此外,BEFORE 触发器的 WHEN 条件不允许检查 NEW 行的系统列(如 ctid),因为这些列还没有设置。

在一个 AFTER 触发器中,WHEN 条件在行更新发生后立即进行评估,它决定是否将一个事件排队,以便在语句结束时触发触发器。 因此,当 AFTER 触发器的 WHEN 条件没有返回 true 时,没有必要排队事件或在语句结束时重新获取行。 如果触发器只需要为几行触发,这可能会导致修改许多行的语句的显著加速。

在某些情况下,单个 SQL 命令可能会触发多种类型的触发器。 例如,具有 ON CONFLICT DO UPDATE 子句的 INSERT 可能会导致插入和更新操作,因此它将根据需要触发两种类型的触发器。 提供给触发器的转换关系特定于它们的事件类型; 因此,INSERT 触发器只会看到插入的行,而 UPDATE 触发器只会看到更新的行。

由外键强制操作(如 ON UPDATE CASCADEON DELETE SET NULL)引起的行的更新或删除被视为引起它们 SQL 命令的一部分(请注意,此类操作永远不会延迟)。 将触发受影响表的相关触发器,因此这提供了另一种方式,即 SQL 命令可能触发与其类型不直接匹配的触发器。 在简单情况下,请求转换关系的触发器将看到由单个原始 SQL 命令在它们表中引起的全部更改,作为一个单一的转换关系。 但是,有些情况下,存在请求转换关系的 AFTER ROW 触发器会导致单个 SQL 命令触发的外键强制操作被拆分为多个步骤,每个步骤都有自己的转换关系集。 在这种情况下,任何存在的语句级触发器将在每次创建转换关系集时触发一次,确保触发器在转换关系中看到每个受影响的行一次且只有一次。

视图上的语句级触发器只有在视图上的操作由行级 INSTEAD OF 触发器处理时才会触发。 如果操作由 INSTEAD 规则处理,则规则发出的任何语句都将代替命名视图的原始语句执行,因此触发的触发器是替换语句中命名的表的触发器。 同样,如果视图是自动可更新的,则该操作将通过自动将语句重写为视图基础表上的操作来处理,因此基础表的语句级触发器是触发器。

修改一个分区表或一个具有继承子表的表将触发附加到显式命名表的语句级触发器,但不触发其分区或子表的语句级触发器。 相反,即使在查询中没有显式命名,行级触发器也会在受影响的分区或子表中的行上触发。 如果一个语句级触发器是使用 REFERENCING 子句命名的转换关系定义的,那么所有受影响的分区或子表的行之前和之后的图像都是可见的。 在继承子表的情况下,行图像仅包括附加触发器所在的表中存在的列。

目前,带有转换关系的行级触发器不能在分区或继承子表上定义。 此外,分区表上的触发器不能是 INSTEAD OF

目前,对于约束触发器,不支持 OR REPLACE 选项。

在一个已经对触发器表的更新操作执行了的交易中,替换现有的触发器是不推荐的。 已经做出的触发器触发决策或部分触发决策将不会被重新考虑,因此其影响可能会令人惊讶。

有一些内置的触发器函数可以用来解决常见问题,而无需编写自己的触发器代码; 请参阅 第 9.29 节

例子

每当要更新表 accounts 的一行时,执行函数 check_account_update

CREATE TRIGGER check_update
    BEFORE UPDATE ON accounts
    FOR EACH ROW
    EXECUTE FUNCTION check_account_update();

修改该触发器定义,只有在 UPDATE 命令中指定列 balance 作为目标时才执行该函数

CREATE OR REPLACE TRIGGER check_update
    BEFORE UPDATE OF balance ON accounts
    FOR EACH ROW
    EXECUTE FUNCTION check_account_update();

这种形式只有在列 balance 的值实际上改变时才执行该函数

CREATE TRIGGER check_update
    BEFORE UPDATE ON accounts
    FOR EACH ROW
    WHEN (OLD.balance IS DISTINCT FROM NEW.balance)
    EXECUTE FUNCTION check_account_update();

调用一个函数来记录 accounts 的更新,但只有在某些内容发生改变时才记录

CREATE TRIGGER log_update
    AFTER UPDATE ON accounts
    FOR EACH ROW
    WHEN (OLD.* IS DISTINCT FROM NEW.*)
    EXECUTE FUNCTION log_account_update();

为每一行执行函数 view_insert_row,以将行插入到视图的基础表中

CREATE TRIGGER view_insert
    INSTEAD OF INSERT ON my_view
    FOR EACH ROW
    EXECUTE FUNCTION view_insert_row();

为每个语句执行函数 check_transfer_balances_to_zero,以确认 transfer 行偏移到净值为零

CREATE TRIGGER transfer_insert
    AFTER INSERT ON transfer
    REFERENCING NEW TABLE AS inserted
    FOR EACH STATEMENT
    EXECUTE FUNCTION check_transfer_balances_to_zero();

为每一行执行函数 check_matching_pairs,以确认更改是在同一时间(通过同一个语句)对匹配对进行的

CREATE TRIGGER paired_items_update
    AFTER UPDATE ON paired_items
    REFERENCING NEW TABLE AS newtab OLD TABLE AS oldtab
    FOR EACH ROW
    EXECUTE FUNCTION check_matching_pairs();

第 37.4 节 包含一个用 C 语言编写的触发器函数的完整示例。

兼容性

PostgreSQL 中的 CREATE TRIGGER 语句实现了以下内容的子集SQL标准。 以下功能目前缺失

  • 虽然 AFTER 触发器的转换表名是使用标准方式的 REFERENCING 子句指定的,但 FOR EACH ROW 触发器中使用的行变量不能在 REFERENCING 子句中指定。 它们以一种依赖于触发器函数编写语言的方式提供,但在任何一种语言中都是固定的。 一些语言的行为实际上就好像有一个 REFERENCING 子句,其中包含 OLD ROW AS OLD NEW ROW AS NEW

  • 标准允许使用转换表与特定于列的 UPDATE 触发器,但转换表中应该可见的行集取决于触发器的列列表。 PostgreSQL 目前没有实现这一点。

  • PostgreSQL 只允许执行用户定义的函数来进行触发操作。 标准允许执行许多其他 SQL 命令,如 CREATE TABLE,作为触发操作。 这种限制很容易通过创建执行所需命令的用户定义函数来解决。

SQL 规定,多个触发器应该按创建时间的顺序触发。 PostgreSQL 使用名称顺序,这被认为更方便。

SQL 规范规定,在级联删除时,BEFORE DELETE 触发器会在级联 DELETE 完成 之后 触发。PostgreSQL 的行为是,BEFORE DELETE 始终在删除操作之前触发,即使是级联删除也是如此。这被认为更加一致。如果 BEFORE 触发器在由引用操作引起的更新期间修改了行或阻止了更新,也会出现非标准行为。这会导致约束违反或存储数据不符合引用约束。

使用 OR 为单个触发器指定多个操作的能力是 PostgreSQL 对 SQL 标准的扩展。

TRUNCATE 触发触发器的能力是 PostgreSQL 对 SQL 标准的扩展,与为视图定义语句级触发器的能力一样。

CREATE CONSTRAINT TRIGGERPostgreSQLSQL标准的扩展。OR REPLACE 选项也是如此。

提交更正

如果您在文档中看到任何不正确的内容,不符合您对特定功能的体验,或者需要进一步说明,请使用 此表格 报告文档问题。