2025年9月25日: PostgreSQL 18 发布!
支持版本:当前 (18) / 17 / 16 / 15 / 14 / 13
开发版本:devel
不支持的版本:12 / 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 命令的目标,或者列是依赖于目标列的生成列时,触发器才会触发。

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

condition

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

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

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

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

function_name

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

CREATE TRIGGER 的语法中,关键字 FUNCTIONPROCEDURE 是等效的,但引用的函数无论如何都必须是函数,而不是过程。此处使用 PROCEDURE 关键字是历史性的,已弃用。

arguments

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

注释

要创建或替换表上的触发器,用户必须拥有该表的 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();

修改该触发器定义,使其仅在 balance 列被指定为 UPDATE 命令的目标时才执行函数。

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 子句中指定。它们以一种依赖于触发器函数所用语言的方式提供,但对于任何一种语言都是固定的。某些语言实际上表现得好像存在一个包含 OLD ROW AS OLD NEW ROW AS NEWREFERENCING 子句。

  • 标准允许将转换表用于列特定的 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 选项也是。

提交更正

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