PostgreSQL 9.4.4 中文手册
上一页 | 上一级 |  | 下一页

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

触发器可以声明为在对记录进行操作之前(在检查约束之前和`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 章获取更多触发器信息。

## 参数

`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();

第 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

上一页 | 起始页 | 下一页
CREATE TEXT SEARCH TEMPLATE | 上一级 | CREATE TYPE