CREATE [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] }
ON table_name
[ FROM referenced_table_name ]
{ NOT DEFERRABLE | [ DEFERRABLE ] { INITIALLY IMMEDIATE | INITIALLY DEFERRED } }
[ FOR [ EACH ] { ROW | STATEMENT } ]
[ WHEN ( condition ) ]
EXECUTE PROCEDURE function_name ( arguments )
where event can be one of:
INSERT
UPDATE [ OF column_name [, ... ] ]
DELETE
TRUNCATE
CREATE TRIGGER创建一个新的触发器。 触发器将与指定表、视图或外部表关联并且将在特定事件发生时执行声明的 function_name函数。
触发器可以声明为在对记录进行操作之前(在检查约束之前和INSERT、 UPDATE、DELETE执行前); 或操作完成之后(在检查约束之后和完成INSERT、 UPDATE、UPDATE操作);或取代操作 (在视图上插入、更新或删除)触发。如果触发器在事件之前或者取代事件, 触发器可能略过当前记录的操作或改变被插入的记录(只对UPDATE 和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。
下面表总结中的触发器类型可能被用在表、视图和外部表上:
何时 | 事件 | 行级别 | 语句级别 |
---|---|---|---|
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强制立即发生。 当它们实现的约束非法时,约束触发器预计会引发一个异常。
SELECT并不更改任何行,因此你不能创建 SELECT触发器。这种场合下规则和视图更合适些。
请参考第 36 章获取更多触发器信息。
赋予新触发器的名称。它必需和任何作用于同一表的触发器不同。 该名字不能是模式修饰的—触发器继承它的表的模式。 对于约束触发器,当使用SET CONSTRAINTS修改触发器的行为时, 这也是要使用的名字。
决定该函数是在事件之前、之后还是取代事件时调用。 约束触发器只能被声明为AFTER。
INSERT、UPDATE、DELETE 或TRUNCATE之一。它声明激发触发器的事件。 多个事件可以用OR声明。
对于UPDATE事件,使用这个语法声明一个字段列表是可能的:
UPDATE OF column_name1 [, column_name2 ... ]
该触发器将只在至少一个列表中的字段在UPDATE命令的目标中提及时触发。
INSTEAD OF UPDATE事件不支持字段的列表。
触发器作用的表、视图或外部表的名称(可以有模式修饰)
约束引用的另外一个表的名字(可以有模式修饰)。这个选项用于外键约束, 不推荐用于一般用途。只能为约束触发器指定。
触发器的默认时机。参阅CREATE TABLE文档获取这些约束选项的详细信息。 只能为约束触发器指定。
这些选项声明触发器过程是否为触发器事件影响的每个行触发一次, 还是只为每条 SQL 语句触发一次。如果都没有声明, 那么FOR EACH STATEMENT将是缺省。 约束触发器只能声明为FOR EACH ROW。
一个决定触发器函数实际上是否执行的布尔表达式。如果声明了WHEN, 那么该函数只有condition 返回true时被调用。在FOR EACH ROW触发器中, WHEN条件可以通过分别写OLD.column_name或NEW.column_name参考字段的旧的和/或新的行值。 当然,INSERT触发器不能参考OLD, OLD触发器不能参考NEW。
INSTEAD OF触发器不支持WHEN条件。
目前,WHEN表达式不能包含子查询。
请注意,对于约束触发器,WHEN条件的计算是不延迟的, 只是在行更新操作执行之后立即发生。如果该条件计算不为真, 那么触发器就不排队延迟执行。
一个用户提供的函数,它声明为不接受参数并且返回trigger类型, 该函数将在触发器被触发时调用。
一个可选的用逗号分隔的参数列表,它将在触发器执行的时候提供给函数。 这些参数是文本字符串常量。也可以在这里写简单的名字和数值常量, 但是它们会被转换成字符串。请检查该触发器函数的实现语言的描述, 找出如何在该函数中访问这些参数;这些参数可能和普通的函数参数不同。
要在表上创建一个触发器,用户必需在该表上有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的函数,但是它将发出一条 NOTICE 并且把函数声明的返回类型改成 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();
为每一行执行函数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 允许你为"old"和"new"行或者表定义别名, 用于定义触发器的动作(也就是CREATE TRIGGER ... ON tablename REFERENCING OLD ROW AS somename NEW ROW AS othername ...)。 因为PostgreSQL 允许触发器过程以任意数量的用户定义语言进行书写, 所以访问数据的工作是用和语言相关的方法实现的。
PostgreSQL不允许旧的和新的表在语句级别的触发器中引用, 也就是,包含所有旧的和/或新的行的表,在SQL标准中被OLD TABLE 和NEW TABLE子句提及。
PostgreSQL只允许为触发的动作执行用户定义的函数。 SQL 标准允许执行一些其它的命令,比如拿CREATE TABLE 作为触发器动作。这个限止并不难绕开,只要创建一个执行这些命令的用户定义的函数即可。
SQL 要求多个触发器应该以创建的时间顺序执行。 PostgreSQL采用的是按照名字顺序,并认为这样更加方便。
SQL 要求必须在级联DELETE完成之后再触发级联删除上的 BEFORE DELETE触发器。PostgreSQL 的行为是BEFORE DELETE将永远在删除动作之前触发, 即使对于级联删除也是如此,我们认为这样更一致。如果BEFORE 触发器在由引用操作引起的更新期间修改行或阻止更新,仍然存在不标准的行为。 这将导致违反约束或者存储不符合参照完整性的数据。
用OR给一个触发器声明多个动作是 PostgreSQL对SQL标准的扩展。
触发触发器TRUNCATE的能力是一个 PostgreSQL对SQL标准的扩展,就像在视图上定义语句级别触发器的能力。
CREATE CONSTRAINT TRIGGER是一个 PostgreSQL对SQL标准的扩展。