CREATE TRIGGER
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 OF
column_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 标准的一个扩展。