On this page
CREATE TRIGGER
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 ] ]
[ REFERENCING { { OLD | NEW } TABLE [ AS ] transition_relation_name } [ ... ] ]
[ FOR [ EACH ] { ROW | STATEMENT } ]
[ WHEN ( condition ) ]
EXECUTE PROCEDURE function_name ( arguments )
where event can be one of:
INSERT
UPDATE [ OF column_name [, ... ] ]
DELETE
TRUNCATE
Description
CREATE TRIGGER
创建一个新触发器。触发器将与指定的表,视图或外部表关联,并且在对该表执行某些操作时将执行指定的功能* function_name
*。
可以将触发器指定为在尝试对该行进行操作之前触发(在检查约束并尝试INSERT
,UPDATE
或DELETE
之前);或在操作完成之后(在检查约束并且INSERT
,UPDATE
或DELETE
完成之后);或代替操作(对于视图执行插入,更新或删除操作)。如果触发器在事件发生之前或代替事件触发,则触发器可以跳过当前行的操作,或更改要插入的行(仅适用于INSERT
和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
。
下表总结了可以在表,视图和外部表上使用的触发器类型:
When | Event | Row-level | Statement-level |
---|---|---|---|
BEFORE |
INSERT / UPDATE / DELETE |
表和外表 | 表格,视图和外部表格 |
TRUNCATE |
— | Tables | |
AFTER |
INSERT / UPDATE / DELETE |
表和外表 | 表格,视图和外部表格 |
TRUNCATE |
— | Tables | |
INSTEAD OF |
INSERT / UPDATE / DELETE |
Views | — |
TRUNCATE |
— | — |
另外,触发器定义可以指定布尔值WHEN
,将对其进行测试以查看是否应触发触发器。在行级触发器中,WHEN
条件可以检查该行的列的旧值和/或新值。语句级触发器也可以具有WHEN
条件,尽管该功能对它们而言并不是那么有用,因为条件不能引用表中的任何值。
如果为同一事件定义了多个相同种类的触发器,则将按名称的字母 Sequences 触发它们。
指定CONSTRAINT
选项后,此命令将创建约束触发器。这与常规触发器相同,除了可以使用SET CONSTRAINTS调整触发器触发的时间。约束触发器必须是普通表(而非外部表)上的AFTER ROW
触发器。它们可以在引起触发事件的语句末尾或包含事务的末尾处触发;在后一种情况下,它们被称为* deferred *。通过使用SET CONSTRAINTS
,也可以立即强制执行待处理的延迟触发点火。当违反约束触发器实现的约束时,预计将引发异常。
REFERENCING
选项启用过渡关系的收集,过渡关系是包括当前 SQL 语句插入,删除或修改的所有行的行集。此功能使触发器可以查看语句执行的全局视图,而不是一次仅查看一行。此选项仅适用于不是约束触发器的AFTER
触发器;同样,如果触发器是UPDATE
触发器,则它不能指定* column_name
列表。 OLD TABLE
只能指定一次,并且只能用于可以在UPDATE
或DELETE
上触发的触发器;它创建一个转换关系,包含该语句更新或删除的所有行的“ before-images”。同样,NEW TABLE
只能指定一次,并且只能用于可以在UPDATE
或INSERT
上触发的触发器;它创建一个转换关系,包含该语句更新或插入的所有行的 after-images *。
SELECT
不修改任何行,因此您无法创建SELECT
触发器。规则和视图可以为似乎需要SELECT
触发器的问题提供可行的解决方案。
有关触发器的更多信息,请参考Chapter 38。
Parameters
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文档。这只能为约束触发器指定。
REFERENCING
- 该关键字紧接在声明一个或两个关系名称之前,该名称提供对触发语句的转换关系的访问。
OLD TABLE
NEW TABLE
- 该子句指示以下关系名称是针对图像之前转换关系还是图像之后转换关系。
transition_relation_name
- 触发器中要为此转换关系使用的(非限定)名称。
FOR EACH ROW
FOR EACH STATEMENT
- 这指定是针对受触发事件影响的每一行触发一次触发过程,还是针对每个 SQL 语句仅触发一次。如果两者均未指定,则默认为
FOR EACH STATEMENT
。约束触发器只能指定FOR EACH ROW
。
- 这指定是针对受触发事件影响的每一行触发一次触发过程,还是针对每个 SQL 语句仅触发一次。如果两者均未指定,则默认为
condition
- 一个布尔表达式,确定是否实际执行触发函数。如果指定了
WHEN
,则仅当*condition
*返回true
时才调用该函数。在FOR EACH ROW
触发器中,通过分别写入OLD.column_name
或NEW.column_name
,WHEN
条件可以引用旧和/或新行值的列。当然,INSERT
触发器不能引用OLD
,而DELETE
触发器不能引用NEW
。
- 一个布尔表达式,确定是否实际执行触发函数。如果指定了
INSTEAD OF
个触发器不支持WHEN
个条件。
当前,WHEN
表达式不能包含子查询。
请注意,对于约束触发器,不推迟对WHEN
条件的评估,而是在执行行更新操作之后立即进行评估。如果条件未评估为真,则触发器不会排队 await 延迟执行。
function_name
- 用户提供的函数,声明为不带任何参数并返回类型
trigger
,该函数在触发器触发时执行。
- 用户提供的函数,声明为不带任何参数并返回类型
arguments
- 执行触发器时,将提供给函数的可选的逗号分隔参数列表。参数是 Literals 字符串常量。简单名称和数字常量也可以在此处编写,但是它们都将转换为字符串。请检查对触发器函数的实现语言的描述,以了解如何在函数中访问这些参数。它可能与普通函数参数不同。
Notes
要在表上创建触发器,用户必须在表上具有TRIGGER
特权。用户还必须具有触发功能的EXECUTE
特权。
使用DROP TRIGGER删除触发器。
当某列特定触发器(使用UPDATE OF column_name
语法定义的触发器)在UPDATE
命令的SET
列表中被列为目标时,将触发该触发器。即使未触发触发器,列的值也可能会更改,因为未考虑通过BEFORE UPDATE
触发器对行的内容所做的更改。相反,即使UPDATE ... SET x = x ...
的值未更改,诸如UPDATE ... SET x = x ...
的命令也会在x
列上触发触发器。
在BEFORE
触发器中,仅在函数即将执行或即将执行WHEN
条件时对其求值,因此使用WHEN
与在触发器函数开始时测试相同条件没有实质性区别。特别要注意的是,条件看到的NEW
行是当前值,可能会被更早的触发器修改。另外,不允许BEFORE
触发器的WHEN
条件检查NEW
行的系统列(例如oid
),因为尚未设置这些列。
在AFTER
触发器中,在发生行更新之后立即评估WHEN
条件,它确定是否将一个事件排队,以在语句结尾触发触发器。因此,当AFTER
触发器的WHEN
条件没有返回 true 时,就不必对事件进行排队,也不必在语句末尾重新获取行。如果只需要为少数几行触发触发器,则可以大大提高修改多行的语句的速度。
在某些情况下,单个 SQL 命令可能会触发一种以上的触发器。例如,带有ON CONFLICT DO UPDATE
子句的INSERT
可能同时引起插入和更新操作,因此它将根据需要触发两种触发器。提供给触发器的过渡关系特定于它们的事件类型。因此,INSERT
触发器将仅看到插入的行,而UPDATE
触发器将仅看到更新的行。
由外键强制操作(例如ON UPDATE CASCADE
或ON DELETE SET NULL
)引起的行更新或删除被视为导致行更新或删除的 SQL 命令的一部分(请注意,此类操作永远不会被延迟)。将触发受影响的表上的相关触发器,从而提供另一种方式,使 SQL 命令可以触发与该类型不直接匹配的触发器。在简单的情况下,请求转换关系的触发器会将单个原始 SQL 命令在其表中引起的所有更改视为一个转换关系。但是,在某些情况下,请求转换关系的AFTER ROW
触发器的存在将导致由单个 SQL 命令触发的外键强制操作分成多个步骤,每个步骤都有自己的转换关系。在这种情况下,每次创建过渡关系集时,都会触发一次存在的任何语句级触发器,以确保触发器一次仅看到一次过渡关系中的每个受影响的行。
仅当视图上的操作由行级INSTEAD OF
触发器处理时,才会触发视图上的语句级触发器。如果操作是由INSTEAD
规则处理的,则将执行规则发出的任何语句来代替为视图命名的原始语句,因此将触发的触发器是替换语句中命名的表上的触发器。同样,如果视图是可自动更新的,则可通过将语句自动重写为视图基表上的操作来处理该操作,以便基表的语句级触发器为被触发的触发器。
修改分区表或具有继承子级的表会触发附加到显式命名表的语句级触发器,但不会触发其分区或子表的语句级触发器。相反,即使在查询中未明确命名行级触发器,也会在受影响的分区或子表中的行上触发行级触发器。如果已使用由REFERENCING
子句命名的转换关系定义了语句级触发器,则在所有受影响的分区或子表中都可以看到行的图像的前后。对于继承子级,行图像仅包含触发器附加到的表中存在的列。当前,不能在分区或继承子表上定义具有转换关系的行级触发器。
在 7.3 之前的 PostgreSQL 版本中,有必要声明触发器函数以返回占位符类型opaque
而不是trigger
。为了支持加载旧的转储文件,CREATE TRIGGER
将接受一个声明为返回opaque
的函数,但它将发出通知并将该函数的声明的返回类型更改为trigger
。
Examples
每当表accounts
的一行将要更新时,执行函数check_account_update
:
CREATE TRIGGER check_update
BEFORE UPDATE ON accounts
FOR EACH ROW
EXECUTE PROCEDURE check_account_update();
相同,但仅在UPDATE
命令中将列balance
指定为目标时才执行该功能:
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();
对每个语句执行函数check_transfer_balances_to_zero
,以确认transfer
行偏移为零的净值:
CREATE TRIGGER transfer_insert
AFTER INSERT ON transfer
REFERENCING NEW TABLE AS inserted
FOR EACH STATEMENT
EXECUTE PROCEDURE check_transfer_balances_to_zero();
对每一行执行函数check_matching_pairs
,以确认同时对匹配对进行更改(通过同一条语句):
CREATE TRIGGER paired_items_update
AFTER UPDATE ON paired_items
REFERENCING NEW TABLE AS newtab OLD TABLE AS oldtab
FOR EACH ROW
EXECUTE PROCEDURE check_matching_pairs();
Section 38.4包含用 C 编写的触发函数的完整示例。
Compatibility
PostgreSQL 中的CREATE TRIGGER
语句实现了 SQL 标准的子集。当前缺少以下功能:
虽然以标准方式使用
REFERENCING
子句指定了AFTER
触发器的转换表名称,但REFERENCING
子句中可能未指定FOR EACH ROW
触发器中使用的行变量。它们的使用方式取决于编写触发功能的语言,但对于任何一种语言都是固定的。某些语言实际上表现得好像有一个包含OLD ROW AS OLD NEW ROW AS NEW
的REFERENCING
子句。该标准允许将过渡表与特定于列的
UPDATE
触发器一起使用,但是过渡表中应可见的行集取决于触发器的列列表。 PostgreSQL 当前未实现此功能。PostgreSQL 仅允许为触发的动作执行用户定义的函数。该标准允许执行许多其他 SQL 命令,例如
CREATE TABLE
,作为触发操作。通过创建执行所需命令的用户定义函数,不难解决此限制。
SQL 指定应按创建时间 Sequences 触发多个触发器。 PostgreSQL 使用名称 Sequences,被认为更方便。
SQL 指定在级联的DELETE
完成后,BEFORE DELETE
触发级联删除会触发火灾。 PostgreSQL 的行为是BEFORE DELETE
始终在删除操作之前触发,甚至是级联操作。这被认为是更一致的。如果BEFORE
触发器触发修改行或阻止在更新期间由引用操作引起的更新,则也存在非标准行为。这可能会导致违反约束或存储的数据不符合参考约束的情况。
使用OR
为单个触发器指定多个操作的功能是 SQL 标准的 PostgreSQL 扩展。
触发TRUNCATE
的触发器的功能是 SQL 标准的 PostgreSQL 扩展,以及在视图上定义语句级触发器的功能。
CREATE CONSTRAINT TRIGGER
是 SQL 标准的 PostgreSQL 扩展。
See Also
ALTER TRIGGER, DROP TRIGGER, CREATE FUNCTION, SET CONSTRAINTS