↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

支持中的版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发中的版本: 19 / 20devel
已结束支持的版本: 13 / 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 / 7.0 / 6.5 / 6.4
历史版本。 PostgreSQL 9.0 已结束支持。 2015-10-08. 请参阅 当前版本手册.

CREATE TRIGGER

CREATE TRIGGER — define a new trigger

大纲

CREATE TRIGGER name { BEFORE | AFTER } { event [ OR ... ] }
    ON table [ FOR [ EACH ] { ROW | STATEMENT } ]
    [ WHEN ( condition ) ]
    EXECUTE PROCEDURE function_name ( arguments )

描述

CREATE TRIGGER创建一个新触发器。触发器会关联到指定的表、视图或外部表,并在发生某些事件时执行指定的函数function_name。

可以指定触发器在尝试对某一行执行该操作之前引发(即在检查约束以及尝试执行INSERT、UPDATE或DELETE之前);也可以在该操作完成之后引发(即在检查约束以及完成 INSERT、UPDATE或 DELETE之后);或者取代该操作执行(用于视图上的插入、更新或删除)。如果触发器在事件之前引发,或者改为取代该事件执行,则它可以跳过对当前行的操作,或者修改待插入的行(仅适用于INSERT和UPDATE操作)。如果触发器在事件之后引发,则所有更改(包括其他触发器的影响)对该触发器都是“可见”的。

被标记为FOR EACH ROW的触发器会针对该操作修改的每一行调用一次。例如,一个影响 10 行的DELETE会导致目标关系上的 ON DELETE触发器分别被调用 10 次,即每个被删除的行调用一次。相比之下,被标记为FOR EACH STATEMENT的触发器对于任意给定操作只执行一次,不管该操作修改了多少行(特别是,修改零行的操作仍会导致所有适用的 FOR EACH STATEMENT触发器执行)。

此外,也可以把触发器定义为在 TRUNCATE 时引发,但只能是 FOR EACH STATEMENT。

此外,触发器定义还可以指定一个布尔型 WHEN 条件,用于测试是否应当引发该触发器。在行级触发器中,WHEN 条件可以检查该行各列的新值和/或旧值。语句级触发器也可以带有 WHEN 条件,不过这一特性对它们的用处不大,因为该条件无法引用表中的任何值。

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

SELECT 不会修改任何行,因此无法创建 SELECT 触发器。对于这类情形,规则和视图更为合适。

参见第 36 章以了解有关触发器的更多信息。

参数

name

要赋予新触发器的名称。它必须不同于同一表上任何其他触发器的名称。

BEFORE
AFTER

决定该函数是在事件之前还是之后被调用。

event

INSERT、UPDATE、DELETE或TRUNCATE之一;它指定将引发该触发器的事件。可以使用OR指定多个事件。

对于UPDATE触发器,可以使用以下语法指定一个列的列表:

UPDATE OF column_name1 [, column_name2 ... ]

只有当列出的列中至少有一列被作为更新的目标提及时,触发器才会引发。

table

该触发器所作用的表、视图或外部表的名称(可以是模式限定的)。

FOR EACH ROW
FOR EACH STATEMENT

这指定触发器过程是针对受触发事件影响的每一行引发一次,还是针对每条 SQL 语句只引发一次。如果两者都未指定,则默认为FOR EACH STATEMENT。

condition

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

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

function_name

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

arguments

一个可选的、以逗号分隔的参数列表,在执行触发器时会传给该函数。这些参数是字符串字面常量。这里也可以写简单名称和数字常量,但它们都会被转换成字符串。请查阅该触发器函数所用实现语言的说明,以了解如何在函数内部访问这些参数;这可能与普通函数参数不同。

注解

要在表上创建触发器,用户必须拥有该表的TRIGGER权限。用户还必须拥有触发器函数的EXECUTE权限。

使用DROP TRIGGER删除触发器。

列限定触发器(即使用 UPDATE OF column_name 语法定义的触发器)会在其任一列被列为 UPDATE 命令的 SET 列表目标时引发。即便触发器没有引发,列值仍有可能发生变化,因为 BEFORE UPDATE 触发器对行内容所作的更改不会被考虑在内。反过来,诸如 UPDATE ... SET x = x ... 这样的命令会引发列 x 上的触发器,即使该列的值实际上并未变化。

在BEFORE触发器中,WHEN条件会在函数执行前(或者本应执行前)立即求值,因此使用WHEN与在触发器函数开头测试相同条件并无实质区别。特别要注意的是,该条件看到的NEW行是当前值,它可能已经被更早触发的触发器修改过。此外,BEFORE触发器的WHEN条件不允许检查NEW行的系统列(例如oid),因为这些列此时尚未被设置。

在 AFTER 触发器中,WHEN 条件会在行变更发生后立即求值,并决定是否将一个事件排入队列,以便在语句结束时引发该触发器。因此,当 AFTER 触发器的 WHEN 条件不返回真时,就没有必要将事件排队,也不需要在语句结束时重新获取该行。如果触发器只需针对少数几行引发,这能显著提升那些会修改很多行的语句的速度。

在PostgreSQL 7.3 之前的版本中,必须将触发器函数声明为返回占位类型opaque,而不是trigger。为了支持加载旧转储文件,CREATE TRIGGER将接受声明为返回opaque的函数,但会发出通知,并将该函数声明的返回类型更改为trigger。

示例

每当表accounts的一行即将被更新时,执行函数check_account_update:

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

相同,但只有当列balance被指定为UPDATE命令中的一个目标时才执行该函数:

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

这种形式只在列balance的值确实发生了改变时才执行该函数:

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

调用一个函数来记录accounts的更新,但只在有变化发生时:

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

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

兼容性

PostgreSQL 中的 CREATE TRIGGER 语句实现了 SQL 标准的一个子集。当前仍缺少以下功能:

  • SQL 允许为在触发动作定义中使用的“旧”行或“新”行定义别名(例如 CREATE TRIGGER ... ON tablename REFERENCING OLD ROW AS somename NEW ROW AS othername ...)。由于 PostgreSQL 允许用任意多种用户定义的语言编写触发器过程,对数据的访问是以语言相关的方式处理的。

  • PostgreSQL 只允许通过执行用户定义函数来完成触发动作。标准则允许把许多其他 SQL 命令(例如 CREATE TABLE)作为触发动作执行。通过创建一个执行所需命令的用户定义函数,这一限制并不难绕过。

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

SQL 规定,级联删除上的 BEFORE DELETE 触发器应在级联 DELETE 完成之后引发。PostgreSQL 的行为则是 BEFORE DELETE 总会在删除动作之前引发,即使是级联删除也一样。这被认为更一致。如果 BEFORE 触发器在由引用动作引起的更新过程中修改行或阻止更新,也会出现非标准行为。这可能导致约束违反,或者使存储的数据不符合引用约束。

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

为TRUNCATE引发触发器的能力是PostgreSQL对 SQL 标准的一个扩展。

报告文档问题

阅读 上游文档. 反馈更正前请先核对 当前版本手册.