Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

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,只能在视图上定义。BEFORE 和视图上的 AFTER 触发器必须标记为 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 章

参数

名称

要赋予新触发器的名称。此名称必须区别于同一表上的任何其他触发器的名称。名称不能限定架构——触发器将继承其表的架构。对于约束触发器,它也是修改使用 SET CONSTRAINTS 修改触发器行为时要使用的名称。

BEFORE
AFTER
INSTEAD OF

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

事件

如果 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

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命令的SET列表中作为目标列时,会触发一个特定于列的触发器(一个使用UPDATE OF column_name语法定义的触发器)。即使没有触发触发器,列的值仍然可能改变,原因是BEFORE UPDATE触发器对行内容所做的更改不被考虑。相反,诸如UPDATE ... SET x = x ...之类的命令将触发列x上的一个触发器,即使该列的值没有改变。

BEFORE触发器中,WHEN条件会在函数被执行或将被执行之前立即被评估,因此使用WHEN从本质上来说与在触发器函数开始时测试相同条件没有什么不同。特别注意条件所看到的NEW行是当前值,而且可能被更早的触发器所修改。另外,BEFORE触发器的WHEN条件不被允许检查NEW行的系统列(例如ctid),因为这些列尚未被设置。

AFTER触发器中,WHEN条件会在行更新发生之后立即被评估,并且它会确定在语句结束时是否对一个事件排队来触发触发器。因此,当AFTER触发器的WHEN条件不返回 true 时,就不必对事件排队或在语句结束时重新获取行。如果触发器只需要为几行触发,那么这可以在修改多行的语句中产生明显的加速。

在某些情况下,一个 SQL 命令可以激发多种类型的触发器。例如,带有 INSERTON 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 子句中指定。它们的可用的方式取决于触发器函数所写的语言,但对任何一种语言而言都是固定的。一些语言有效地表现得好像有一个 REFERENCING 子句,其中包含 OLD ROW AS OLD NEW ROW AS NEW

  • 标准允许将转换表与特定于列的 UPDATE 触发器一起使用,但转换表中应显示的那组行取决于该触发器的列列表。当前,PostgreSQL 没有实现这一功能。

  • PostgreSQL 只允许对触发操作执行用户定义的函数。而标准允许将多个其他 SQL 命令执行作为触发操作,例如 CREATE TABLE。通过创建一个执行所需命令的用户定义函数,可以轻松解决此限制。

SQL 规定应按创建时间顺序触发 multiple triggers。 PostgreSQL 使用更方便的名称顺序。

SQL 规定,级联删除上的 BEFORE DELETE 触发器应在级联 DELETE 完成时 之后 触发PostgreSQL 的行为是 BEFORE DELETE 始终在删除操作之前触发,甚至在级联删除时也是如此。这被认为更一致。当 BEFORE 触发器修改行或因外键操作导致的更新中禁止更新时,也会出现非标准行为。这可能导致约束冲突或不遵守外键约束的存储数据。

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

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

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