CREATE TRIGGER

名称

CREATE TRIGGER -- 定义一个新触发器

大纲

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函数。

触发器可以声明为在对记录进行操作之前(在检查约束之前和INSERTUPDATEDELETE执行前); 或操作完成之后(在检查约束之后和完成INSERTUPDATEUPDATE操作);或取代操作 (在视图上插入、更新或删除)触发。如果触发器在事件之前或者取代事件, 触发器可能略过当前记录的操作或改变被插入的记录(只对UPDATEUPDATE操作有效)。如果触发器在事件之后, 所有更改,包括其他触发器的影响,对触发器都是"可见"的。

一个被标记为FOR EACH ROW的触发器为操作修改的每一行都调用一次。 比如,一个影响 10 行的DELETE将导致任何在目标关系上的 ON DELETE触发器独立调用 10 次,每个被删除的行调用一次。 相比之下,一个被标记为FOR EACH STATEMENT的触发器只执行一次, 而不管有多少行被修改。(特别是,一个修改零行的操作仍然会导致合适的 FOR EACH STATEMENT触发器被执行。

指定为触发INSTEAD OF触发器事件的触发器必须被标记为 FOR EACH ROW,并且只能在视图上定义。视图上的BEFOREAFTER触发器必须被标记为FOR EACH STATEMENT

另外,触发器可能被定义为为TRUNCATE触发, 尽管只有FOR EACH STATEMENT

下面表总结中的触发器类型可能被用在表和视图上:

何时事件行级别语句级别
BEFOREINSERT/UPDATE/DELETE表和视图
TRUNCATE
AFTERINSERT/UPDATE/DELETE表和视图
TRUNCATE
INSTEAD OFINSERT/UPDATE/DELETE视图
TRUNCATE

还有,触发器定义可以声明一个布尔WHEN条件,用来测试触发器是否应该被触发。 在行级别触发器中,WHEN条件可以检测该行的字段的旧的和/或新值。 语句级别的触发器也可以拥有WHEN条件,尽管该特性对于它们来说不太有用, 因为该条件不能引用表中的任何值。

如果多个同类型的触发器为同一事件做了定义,那么它们将按照字母顺序被触发。

当声明了CONSTRAINT选项时,这个命令创建一个约束触发器。 作为正规触发器也是相同的,除了触发器触发的时间可以使用SET CONSTRAINTS 调整。约束触发器必须是AFTER ROW触发器。 它们可以在导致触发事件的语句的结束触发,也可以在包含的事务的结束触发; 在后面一种情况下,它们被称为延迟的。 一个等待延迟的触发器触发也可以通过使用SET CONSTRAINTS强制立即发生。 当它们实现的约束非法时,约束触发器预计会引发一个异常。

SELECT并不更改任何行,因此你不能创建 SELECT触发器。这种场合下规则和视图更合适些。

请参考第 36 章获取更多触发器信息。

参数

name

赋予新触发器的名称。它必需和任何作用于同一表的触发器不同。 该名字不能是模式修饰的—触发器继承它的表的模式。 对于约束触发器,当使用SET CONSTRAINTS修改触发器的行为时, 这也是要使用的名字。

BEFORE
AFTER
INSTEAD OF

决定该函数是在事件之前、之后还是取代事件时调用。 约束触发器只能被声明为AFTER

event

INSERTUPDATEDELETETRUNCATE之一。它声明激发触发器的事件。 多个事件可以用OR声明。

对于UPDATE事件,使用这个语法声明一个字段列表是可能的:

UPDATE OF column_name1 [, column_name2 ... ]

该触发器将只在至少一个列表中的字段在UPDATE命令的目标中提及时触发。

INSTEAD OF UPDATE事件不支持字段的列表。

table_name

触发器作用的表或视图的名称(可以有模式修饰)

referenced_table_name

约束引用的另外一个表的名字(可以有模式修饰)。这个选项用于外键约束, 不推荐用于一般用途。只能为约束触发器指定。

DEFERRABLE
NOT DEFERRABLE
INITIALLY IMMEDIATE
INITIALLY DEFERRED

触发器的默认时机。参阅CREATE TABLE文档获取这些约束选项的详细信息。 只能为约束触发器指定。

FOR EACH ROW
FOR EACH STATEMENT

这些选项声明触发器过程是否为触发器事件影响的每个行触发一次, 还是只为每条 SQL 语句触发一次。如果都没有声明, 那么FOR EACH STATEMENT将是缺省。 约束触发器只能声明为FOR EACH ROW

condition

一个决定触发器函数实际上是否执行的布尔表达式。如果声明了WHEN, 那么该函数只有condition 返回true时被调用。在FOR EACH ROW触发器中, WHEN条件可以通过分别写OLD.column_nameNEW.column_name参考字段的旧的和/或新的行值。 当然,INSERT触发器不能参考OLDOLD触发器不能参考NEW

INSTEAD OF触发器不支持WHEN条件。

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

请注意,对于约束触发器,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的函数,但是它将发出一条 NOTICE 并且把函数声明的返回类型改成 trigger

例子

当表accounts的一行要被更新时,执行函数 check_account_update

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

同样的,但是只在字段balanceUPDATE 命令的目标中指定时执行该函数:

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 要求多个触发器应该以创建的时间顺序执行。 PostgreSQL采用的是按照名字顺序,并认为这样更加方便。

SQL 要求必须在级联DELETE完成之后再触发级联删除上的 BEFORE DELETE触发器。PostgreSQL 的行为是BEFORE DELETE将永远在删除动作之前触发, 即使对于级联删除也是如此,我们认为这样更一致。如果BEFORE 触发器在由引用操作引起的更新期间修改行或阻止更新,仍然存在不标准的行为。 这将导致违反约束或者存储不符合参照完整性的数据。

OR给一个触发器声明多个动作是 PostgreSQL对SQL标准的扩展。

触发触发器TRUNCATE的能力是一个 PostgreSQL对SQL标准的扩展,就像在视图上定义语句级别触发器的能力。

CREATE CONSTRAINT TRIGGER是一个 PostgreSQLSQL标准的扩展。

又见

ALTER TRIGGER, DROP TRIGGER, CREATE FUNCTION, SET CONSTRAINTS