多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
# 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](#calibre_link-85) 调整。约束触发器必须是`AFTER ROW`触发器。 它们可以在导致触发事件的语句的结束触发,也可以在包含的事务的结束触发; 在后面一种情况下,它们被称为_延迟的_。 一个等待延迟的触发器触发也可以通过使用`SET CONSTRAINTS`强制立即发生。 当它们实现的约束非法时,约束触发器预计会引发一个异常。 `SELECT`并不更改任何行,因此你不能创建 `SELECT`触发器。这种场合下规则和视图更合适些。 请参考[Chapter 36](#calibre_link-460)获取更多触发器信息。 ## 参数 `_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](#calibre_link-7)文档获取这些约束选项的详细信息。 只能为约束触发器指定。 `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](#calibre_link-461)删除触发器。 字段特有的触发器(使用`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](#calibre_link-462)包含一个完整的用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](#calibre_link-463), [DROP TRIGGER](#calibre_link-461), [CREATE FUNCTION](#calibre_link-4), [SET CONSTRAINTS](#calibre_link-85)