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 { FUNCTION | PROCEDURE } function_name ( arguments )

where event can be one of:

    INSERT
    UPDATE [ OF column_name [, ... ] ]
    DELETE
    TRUNCATE

Description

CREATE TRIGGER创建一个新触发器。触发器将与指定的表,视图或外部表关联,并且在对该表执行某些操作时将执行指定的功能* function_name *。

可以将触发器指定为在尝试对该行进行操作之前触发(在检查约束并尝试INSERTUPDATEDELETE之前);或在操作完成之后(在检查约束并且INSERTUPDATEDELETE完成之后);或代替操作(对于视图执行插入,更新或删除操作)。如果触发器在事件发生之前或代替事件触发,则触发器可以跳过当前行的操作,或更改要插入的行(仅适用于INSERTUPDATE操作)。如果触发器在事件发生后触发,则所有更改(包括其他触发器的效果)对触发器都是“可见的”。

对于操作修改的每一行,都将一次调用标记为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

下表总结了可以在表,视图和外部表上使用的触发器类型:

WhenEventRow-levelStatement-level
BEFOREINSERT / UPDATE / DELETE表和外表表格,视图和外部表格
TRUNCATETables
AFTERINSERT / UPDATE / DELETE表和外表表格,视图和外部表格
TRUNCATETables
INSTEAD OFINSERT / UPDATE / DELETEViews
TRUNCATE

另外,触发器定义可以指定布尔值WHEN,将对其进行测试以查看是否应触发触发器。在行级触发器中,WHEN条件可以检查该行的列的旧值和/或新值。语句级触发器也可以具有WHEN条件,尽管该功能对它们而言并不是那么有用,因为条件不能引用表中的任何值。

如果为同一事件定义了多个相同种类的触发器,则将按名称的字母 Sequences 触发它们。

指定CONSTRAINT选项后,此命令将创建约束触发器。这与常规触发器相同,除了可以使用SET CONSTRAINTS调整触发器触发的时间。约束触发器必须是普通表(而非外部表)上的AFTER ROW触发器。它们可以在引起触发事件的语句末尾或包含事务的末尾处触发;在后一种情况下,它们被称为* deferred *。通过使用SET CONSTRAINTS,也可以立即强制执行待处理的延迟触发点火。当违反约束触发器实现的约束时,预计将引发异常。

REFERENCING选项启用过渡关系的收集,过渡关系是包括当前 SQL 语句插入,删除或修改的所有行的行集。此功能使触发器可以查看语句执行的全局视图,而不是一次仅查看一行。此选项仅适用于不是约束触发器的AFTER触发器;同样,如果触发器是UPDATE触发器,则它不能指定* column_name 列表。 OLD TABLE只能指定一次,并且只能用于可以在UPDATEDELETE上触发的触发器;它创建一个转换关系,包含该语句更新或删除的所有行的“ before-images”。同样,NEW TABLE只能指定一次,并且只能用于可以在UPDATEINSERT上触发的触发器;它创建一个转换关系,包含该语句更新或插入的所有行的 after-images *。

SELECT不修改任何行,因此您无法创建SELECT触发器。规则和视图可以为似乎需要SELECT触发器的问题提供可行的解决方案。

有关触发器的更多信息,请参考Chapter 39

Parameters

  • 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文档。这只能为约束触发器指定。
  • REFERENCING

    • 该关键字紧接在声明一个或两个关系名称之前,该名称提供对触发语句的转换关系的访问。
  • OLD TABLE
    NEW TABLE

    • 该子句指示以下关系名称是针对图像之前转换关系还是图像之后转换关系。
  • transition_relation_name

    • 触发器中要为此转换关系使用的(非限定)名称。
  • FOR EACH ROW
    FOR EACH STATEMENT

    • 这指定是针对受触发事件影响的每一行触发一次触发函数,还是针对每个 SQL 语句仅触发一次。如果两者均未指定,则默认为FOR EACH STATEMENT。约束触发器只能指定FOR EACH ROW
  • condition

    • 一个布尔表达式,确定是否实际执行触发函数。如果指定了WHEN,则仅当* condition *返回true时才调用该函数。在FOR EACH ROW触发器中,通过分别写入OLD.column_nameNEW.column_nameWHEN条件可以引用旧和/或新行值的列。当然,INSERT触发器不能引用OLD,而DELETE触发器不能引用NEW

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

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

请注意,对于约束触发器,不推迟对WHEN条件的评估,而是在执行行更新操作之后立即进行评估。如果条件未评估为真,则触发器不会排队 await 延迟执行。

  • function_name

    • 用户提供的函数,声明为不带任何参数并返回类型trigger,该函数在触发器触发时执行。

CREATE TRIGGER的语法中,关键字FUNCTIONPROCEDURE是等效的,但是在任何情况下,引用的函数都必须是函数,而不是过程。关键字PROCEDURE的使用在历史上已被弃用。

  • 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 CASCADEON DELETE SET NULL)引起的行更新或删除被视为导致行更新或删除的 SQL 命令的一部分(请注意,此类操作永远不会被延迟)。将触发受影响的表上的相关触发器,以便提供另一种方式,使 SQL 命令可以触发与该类型不直接匹配的触发器。在简单的情况下,请求转换关系的触发器会将单个原始 SQL 命令在其表中引起的所有更改视为一个转换关系。但是,在某些情况下,请求转换关系的AFTER ROW触发器的存在将导致由单个 SQL 命令触发的外键强制操作分成多个步骤,每个步骤都有自己的转换关系。在这种情况下,每次创建过渡关系集时,都会触发一次存在的任何语句级触发器,以确保触发器一次仅看到一次过渡关系中的每个受影响的行。

仅当视图上的操作由行级INSTEAD OF触发器处理时,才会触发视图上的语句级触发器。如果操作是由INSTEAD规则处理的,则将执行规则发出的任何语句来代替为视图命名的原始语句,从而将触发的触发器是替换语句中命名的表上的触发器。同样,如果视图是可自动更新的,则通过将语句自动重写为视图基表上的操作来处理该操作,以便基表的语句级触发器是被触发的触发器。

在分区表上创建行级触发器将导致在其所有现有分区中创建相同的触发器。并且以后创建或附加的任何分区也将包含相同的触发器。如果该分区与其父分区分离,则触发器将被删除。分区表上的触发器只能是AFTER

修改分区表或具有继承子级的表会触发附加到显式命名表的语句级触发器,但不会触发其分区或子表的语句级触发器。相反,即使在查询中未明确命名行级触发器,也会在受影响的分区或子表中的行上触发行级触发器。如果已使用由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 FUNCTION check_account_update();

相同,但仅在UPDATE命令中将列balance指定为目标时才执行该功能:

CREATE TRIGGER check_update
    BEFORE UPDATE OF balance ON accounts
    FOR EACH ROW
    EXECUTE FUNCTION check_account_update();

仅当balance列的值实际上已更改时,此表单才执行该功能:

CREATE TRIGGER check_update
    BEFORE UPDATE ON accounts
    FOR EACH ROW
    WHEN (OLD.balance IS DISTINCT FROM NEW.balance)
    EXECUTE FUNCTION check_account_update();

调用一个函数以记录accounts的更新,但仅在发生更改的情况下:

CREATE TRIGGER log_update
    AFTER UPDATE ON accounts
    FOR EACH ROW
    WHEN (OLD.* IS DISTINCT FROM NEW.*)
    EXECUTE FUNCTION log_account_update();

对每一行执行功能view_insert_row,以将行插入视图基础的表中:

CREATE TRIGGER view_insert
    INSTEAD OF INSERT ON my_view
    FOR EACH ROW
    EXECUTE FUNCTION 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 FUNCTION 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 FUNCTION check_matching_pairs();

Section 39.4包含用 C 编写的触发函数的完整示例。

Compatibility

PostgreSQL 中的CREATE TRIGGER语句实现了 SQL 标准的子集。当前缺少以下功能:

  • 虽然以标准方式使用REFERENCING子句指定了AFTER触发器的转换表名称,但REFERENCING子句中可能未指定FOR EACH ROW触发器中使用的行变量。它们的使用方式取决于编写触发功能的语言,但对于任何一种语言都是固定的。某些语言实际上表现得好像有一个包含OLD ROW AS OLD NEW ROW AS NEWREFERENCING子句。

  • 该标准允许将过渡表与特定于列的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