pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
CREATE TRIGGER — define a new trigger
CREATE TRIGGERname{ BEFORE | AFTER } {event[ OR ... ] } ONtable[ FOR [ EACH ] { ROW | STATEMENT } ] [ WHEN (condition) ] EXECUTE PROCEDUREfunction_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要赋予新触发器的名称。它必须不同于同一表上任何其他触发器的名称。
BEFOREAFTER决定该函数是在事件之前还是之后被调用。
eventINSERT、UPDATE、DELETE或TRUNCATE之一;它指定将引发该触发器的事件。可以使用OR指定多个事件。
对于UPDATE触发器,可以使用以下语法指定一个列的列表:
UPDATE OFcolumn_name1[,column_name2... ]
只有当列出的列中至少有一列被作为更新的目标提及时,触发器才会引发。
table该触发器所作用的表、视图或外部表的名称(可以是模式限定的)。
FOR EACH ROWFOR EACH STATEMENT这指定触发器过程是针对受触发事件影响的每一行引发一次,还是针对每条 SQL 语句只引发一次。如果两者都未指定,则默认为FOR EACH STATEMENT。
condition一个布尔表达式,用于决定触发器函数是否会实际执行。 如果指定了WHEN,则仅当 condition 返回 true 时才会调用该函数。在 FOR EACH ROW 触发器中,WHEN 条件可以分别写成 OLD. 或 column_nameNEW. 来引用旧行值和/或新行值中的列。当然, column_nameINSERT 触发器不能引用 OLD, 而 DELETE 触发器不能引用 NEW。
当前,WHEN 表达式不能包含子查询。
function_name一个由用户提供的函数,声明为不接受参数并返回 trigger 类型;当触发器被触发时,就会执行该函数。
arguments一个可选的、以逗号分隔的参数列表,在执行触发器时会传给该函数。 这些参数是字符串字面常量。这里也可以写简单名称和数字常量, 但它们都会被转换成字符串。请查阅该触发器函数所用实现语言的说明, 以了解如何在函数内部访问这些参数;这可能与普通函数参数不同。
要在表上创建触发器,用户必须拥有该表的TRIGGER权限。用户还必须拥有触发器函数的EXECUTE权限。
使用DROP TRIGGER删除触发器。
列限定触发器(即使用 UPDATE OF 语法定义的触发器)会在其任一列被列为 column_nameUPDATE 命令的 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 标准的一个扩展。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。