CREATE TRIGGER
Name
CREATE TRIGGER -- 定义一个新触发器
Synopsis
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
触发器。这种场合下规则和视图更合适些。
请参考Chapter 36获取更多触发器信息。
参数
_name_
赋予新触发器的名称。它必需和任何作用于同一表的触发器不同。 该名字不能是模式修饰的—触发器继承它的表的模式。 对于约束触发器,当使用SET CONSTRAINTS
修改触发器的行为时, 这也是要使用的名字。
BEFORE``AFTER
INSTEAD OF
决定该函数是在事件之前、之后还是取代事件时调用。 约束触发器只能被声明为AFTER
。
_event_
INSERT
、UPDATE
、DELETE
或TRUNCATE
之一。它声明激发触发器的事件。 多个事件可以用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_name_
或NEW.``_column_name_
参考字段的旧的和/或新的行值。 当然,INSERT
触发器不能参考OLD
, OLD
触发器不能参考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();
同样的,但是只在字段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();
Section 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标准的扩展。
又见
ALTER TRIGGER, DROP TRIGGER, CREATE FUNCTION, SET CONSTRAINTS