pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
CREATE TRIGGER — define a new trigger
CREATE [ CONSTRAINT ] TRIGGERname{ BEFORE | AFTER | INSTEAD OF } {event[ OR ... ] } ONtable[ FROMreferenced_table_name] [ NOT DEFERRABLE | [ DEFERRABLE ] { INITIALLY IMMEDIATE | INITIALLY DEFERRED } ] [ FOR [ EACH ] { ROW | STATEMENT } ] [ WHEN (condition) ] EXECUTE PROCEDUREfunction_name(arguments) whereeventcan be one of: INSERT UPDATE [ OFcolumn_name[, ... ] ] DELETE TRUNCATE
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触发器执行)。
被指定为取代该触发事件执行的 INSTEAD OF 触发器必须标记为 FOR EACH ROW,并且只能定义在视图上。视图上的 BEFORE 和 AFTER 触发器必须标记为 FOR EACH STATEMENT。
此外,也可以把触发器定义为在 TRUNCATE 时引发,但只能是 FOR EACH STATEMENT。
下表汇总了哪些类型的触发器可用于表、视图和外部表:
| When | Event | Row-level | Statement-level |
|---|---|---|---|
BEFORE |
INSERT/UPDATE/DELETE |
Tables | Tables and views |
TRUNCATE |
— | Tables | |
AFTER |
INSERT/UPDATE/DELETE |
Tables | Tables and views |
TRUNCATE |
— | Tables | |
INSTEAD OF |
INSERT/UPDATE/DELETE |
Views | — |
TRUNCATE |
— | — |
此外,触发器定义还可以指定一个布尔型 WHEN 条件, 用于测试是否应当引发该触发器。在行级触发器中, WHEN 条件可以检查该行各列的新值和/或旧值。语句级触发器也可以带有 WHEN 条件,不过这一特性对它们的用处不大,因为该条件无法引用表中的任何值。
如果针对同一事件定义了多个同类触发器,它们将按名称的字母顺序引发。
指定CONSTRAINT选项时,此命令会创建约束触发器。这与普通触发器相同,只是可以使用SET CONSTRAINTS调整触发器触发的时间。约束触发器必须是表上的AFTER ROW触发器。它们可以在导致触发事件的语句结束时触发,也可以在包含该语句的事务结束时触发;后一种情况下称为延迟。也可以使用SET CONSTRAINTS强制尚待执行的延迟触发器立即触发。约束触发器应在其实现的约束被违反时引发异常。
SELECT 不会修改任何行,因此无法创建 SELECT 触发器。对于这类情形,规则和视图更为合适。
参见第 36 章以了解有关触发器的更多信息。
name新触发器的名称。它必须不同于同一表上的任何其他触发器名称。 该名称不能是模式限定名 — 触发器会继承其所属表的模式。 对于约束触发器,这也是使用SET CONSTRAINTS 修改触发器行为时所使用的名称。
BEFOREAFTERINSTEAD OF决定该函数是在事件之前、之后调用,还是取代该事件执行。 约束触发器只能指定为AFTER。
eventINSERT、UPDATE、DELETE或 TRUNCATE 之一;它指定了哪个事件将引发该触发器。 可以使用OR指定多个事件。
对于 UPDATE 事件,可以使用以下语法指定列列表:
UPDATE OFcolumn_name1[,column_name2... ]
只有当列出的列中至少有一列被列为 UPDATE 命令的目标时,触发器才会触发。
INSTEAD OF UPDATE 事件不支持列列表。
table该触发器所作用的表、视图或外部表的名称(可以是模式限定的)。
referenced_table_name约束所引用的另一张表的名称(可以是模式限定的)。 此选项用于外键约束,不建议用于一般用途。它只能为约束触发器指定。
DEFERRABLENOT DEFERRABLEINITIALLY IMMEDIATEINITIALLY DEFERRED触发器的默认时机。有关这些约束选项的细节,请参见 CREATE TABLE。它们只能为约束触发器指定。
FOR EACH ROWFOR EACH STATEMENT这指定触发器过程应针对触发事件影响的每一行触发一次,还是仅针对每条 SQL 语句触发一次。如果两者均未指定,则默认值为FOR EACH STATEMENT。约束触发器只能指定FOR EACH ROW。
condition一个布尔表达式,用于决定触发器函数是否会实际执行。 如果指定了WHEN,则仅当 condition 返回 true 时才会调用该函数。在 FOR EACH ROW 触发器中,WHEN 条件可以分别写成 OLD. 或 column_nameNEW. 来引用旧行值和/或新行值中的列。当然, column_nameINSERT 触发器不能引用 OLD, 而 DELETE 触发器不能引用 NEW。
INSTEAD OF触发器不支持WHEN条件。
当前,WHEN 表达式不能包含子查询。
请注意,对于约束触发器,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。
执行该函数check_account_update,每当表accounts中的一行即将被更新时调用它:
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();
执行该函数view_insert_row,针对每一行将行插入视图底层的表中:
CREATE TRIGGER view_insert
INSTEAD OF INSERT ON my_view
FOR EACH ROW
EXECUTE PROCEDURE view_insert_row();
第 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 标准的扩展; 能够在视图上定义语句级触发器也是如此。
CREATE CONSTRAINT TRIGGER是PostgreSQL对SQL标准的扩展。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。