# 创建触发器

CREATE TRIGGER — 定义一个新的触发器

# 概要

CREATE [ OR REPLACE ] [ 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

# 描述

创建触发器创建一个新的触发器。创建或替换触发器将创建一个新触发器,或替换现有触发器。触发器将与指定的表、视图或外部表相关联,并将执行指定的函数*函数名*当对该表执行某些操作时。

要替换现有触发器的当前定义,请使用创建或替换触发器,指定现有触发器的名称和父表。所有其他属性都被替换。

可以指定触发器在对行尝试操作之前触发(在检查约束和插入,更新, 或者删除已尝试);或在操作完成后(在检查约束和插入,更新, 或者删除已完成);或代替操作(在视图上插入、更新或删除的情况下)。如果触发器在事件之前触发或代替事件触发,则触发器可以跳过当前行的操作,或者更改正在插入的行(例如插入更新仅限操作)。如果触发器在事件之后触发,则所有更改,包括其他触发器的效果,对触发器都是“可见的”。

被标记的触发器每一行对操作修改的每一行调用一次。例如,一个删除影响 10 行将导致任何删除时触发目标关系被调用 10 次,每个删除的行调用一次。相反,被标记的触发器对于每个声明对任何给定操作只执行一次,无论它修改了多少行(特别是,修改零行的操作仍将导致执行任何适用的对于每个声明触发器)。

指定触发的触发器代替必须标记触发事件每一行,并且只能在视图上定义。视图上的触发器必须标记为对于每个声明.

此外,触发器可以定义为触发截短, 虽然只有对于每个声明.

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

什么时候 事件 行级 语句级
之前 插入/使现代化/删去 桌子和外国桌子 表、视图和外部表
截断 桌子
之后 插入/使现代化/删去 桌子和外国桌子 表、视图和外部表
截断 桌子
而不是 插入/使现代化/删去 意见
截断

此外,触发器定义可以指定布尔值什么时候条件,将对其进行测试,以确定是否应触发触发器。行内级别触发什么时候条件可以检查行中列的旧值和/或新值。语句级触发器也可以具有什么时候条件,尽管由于条件不能引用表中的任何值,该功能对它们没有多大用处。

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

限制选项时,此命令将创建约束触发器。这与常规触发器相同,只是触发器触发的时间可以使用设置约束条件.约束触发器必须是赛后普通表(非外部表)上的触发器。它们可以在引发触发事件的语句末尾或包含事务的末尾被激发;在后一种情况下,它们被称为推迟。也可以通过使用设置约束条件。当约束触发器实现的约束被违反时,它们会引发异常。

参考选项启用收集过渡关系,它们是包含当前 SQL 语句插入、删除或修改的所有行的行集。此功能使触发器可以查看语句所做操作的全局视图,而不仅仅是一次一行。此选项仅适用于不是约束触发器的触发器;此外,如果触发器是更新触发器,它不能指定一个*列名列表。旧表只能指定一次,并且只能用于可以触发的触发器更新要么删除;它创建一个包含前像由语句更新或删除的所有行。相似地,新表只能指定一次,并且只能用于可以触发的触发器更新要么插入;它创建一个包含后像*由语句更新或插入的所有行。

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

参考第39章有关触发器的更多信息。

# 参数

姓名

赋予新触发器的名称。这必须与同一表的任何其他触发器的名称不同。该名称不能是模式限定的——触发器继承其表的模式。对于约束触发器,这也是在修改触发器的行为时使用的名称设置约束.



代替

确定函数是在事件之前、之后还是代替事件调用。约束触发器只能指定为.

事件

之一插入,更新,删除, 要么截短;这指定将触发触发器的事件。可以使用指定多个事件要么,除非请求转换关系。

为了更新事件,可以使用以下语法指定列列表:

UPDATE OF column_name1 [, column_name2 ... ]

仅当列出的列中至少有一个列被提及为更新命令,或者列出的列之一是生成的列,该列依赖于作为更新.

而不是更新事件不允许列列表。请求转换关系时也不能指定列列表。

表名

触发器所针对的表、视图或外部表的名称(可选的模式限定)。

参考表名

约束引用的另一个表的(可能是模式限定的)名称。此选项用于外键约束,不推荐用于一般用途。这只能为约束触发器指定。

可延期的
不可延期
最初立即
最初推迟

触发的默认时间。见创建表有关这些约束选项的详细信息的文档。这只能为约束触发器指定。

参考

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

旧表
新表

该子句指示以下关系名称是用于前图像转换关系还是用于后图像转换关系。

过渡关系名称

要在此转换关系的触发器中使用的(非限定)名称。

每一行
对于每个声明

这指定触发器函数是否应该为受触发器事件影响的每一行触发一次,或者每个 SQL 语句只触发一次。如果两者都没有指定,对于每个声明是默认值。只能指定约束触发器每一行.

(健康)状况

一个布尔表达式,用于确定触发器函数是否将实际执行。如果什么时候指定时,仅在以下情况下调用该函数*(健康)状况*返回真的.在每一行触发器,什么时候条件可以通过写入来引用旧和/或新行值的列老的。*列名*或者新的。*列名*分别。当然,插入触发器不能引用老的删除触发器不能引用新的.

代替触发器不支持什么时候状况。

现在,什么时候表达式不能包含子查询。

请注意,对于约束触发器,评估什么时候条件不会延迟,而是在执行行更新操作后立即发生。如果条件未计算为真,则触发器不会排队等待延迟执行。

函数名

用户提供的函数,声明为不带参数并返回类型扳机,在触发器触发时执行。

在语法中创建触发器, 关键词功能程序是等价的,但引用的函数在任何情况下都必须是函数,而不是过程。关键字的使用程序这里是历史的和已弃用的。

论据

执行触发器时提供给函数的可选的逗号分隔参数列表。参数是文字字符串常量。简单的名称和数字常量也可以在这里写入,但它们都将转换为字符串。请检查触发器函数的实现语言的描述,以了解如何在函数内访问这些参数;它可能与普通函数参数不同。

# 笔记

要在表上创建或替换触发器,用户必须拥有扳机桌子上的特权。用户还必须有执行触发功能的特权。

采用下降触发器删除触发器。

在分区表上创建行级触发器将导致在其每个现有分区上创建相同的“克隆”触发器;并且稍后创建或附加的任何分区也将具有相同的触发器。如果子分区上已经存在命名冲突的触发器,则会发生错误,除非创建或替换触发器使用,在这种情况下,该触发器将替换为克隆触发器。当一个分区与其父分区分离时,它的克隆触发器将被删除。

特定于列的触发器(使用更新 *列名*syntax) 将在其任何列被列为目标时触发更新命令的列表。即使没有触发触发器,列的值也可能发生变化,因为对行内容所做的更改是更新前不考虑触发器。相反,一个命令,如更新 ... 设置 x = x ...将在列上触发触发器x,即使列的值没有改变。

在一个触发器什么时候在函数即将执行或将要执行之前评估条件,因此使用什么时候与在触发函数开始时测试相同条件没有本质区别。特别注意,新的条件看到的行是当前值,可能已被早期触发器修改。也触发器的什么时候条件不允许检查系统列新的行(例如ctid),因为这些还没有设置。

在一个触发器什么时候在行更新发生后立即评估条件,它确定事件是否排队以在语句结束时触发触发器。所以当一个触发器的什么时候条件不返回 true,不需要将事件排队,也不需要在语句末尾重新获取行。如果只需要为少数几行触发触发器,这可能会导致修改多行的语句显着加速。

在某些情况下,单个 SQL 命令可能会触发多种触发器。例如一个插入冲突时做更新子句可能会导致插入和更新操作,因此它会根据需要触发这两种触发器。提供给触发器的转换关系特定于它们的事件类型;因此一个插入触发器将只看到插入的行,而更新触发器将只看到更新的行。

由外键强制操作引起的行更新或删除,例如更新级联要么删除设置为空, 被视为导致它们的 SQL 命令的一部分(请注意,此类操作永远不会延迟)。受影响表上的相关触发器将被触发,因此这提供了另一种方式,SQL 命令可能会触发与其类型不直接匹配的触发器。在简单的情况下,请求转换关系的触发器会将单个原始 SQL 命令在其表中引起的所有更改视为单个转换关系。但是,在某些情况下,存在行后请求转换关系的触发器将导致由单个 SQL 命令触发的外键强制操作被拆分为多个步骤,每个步骤都有自己的转换关系。在这种情况下,存在的任何语句级触发器将在每次创建转换关系集时触发一次,确保触发器在转换关系中看到每个受影响的行一次且仅一次。

仅当视图上的操作由行级处理时才会触发视图上的语句级触发器代替扳机。如果动作由一个反而规则,然后该规则发出的任何语句都将代替命名视图的原始语句执行,因此将触发的触发器是替换语句中命名的表上的触发器。类似地,如果视图是自动可更新的,则通过自动将语句重写为视图基表上的操作来处理该操作,以便触发基表的语句级触发器。

修改分区表或具有继承子表的表会触发附加到显式命名表的语句级触发器,但不会触发其分区或子表的语句级触发器。相反,行级触发器在受影响的分区或子表中的行上触发,即使它们没有在查询中显式命名。如果语句级触发器已使用由 a 命名的转换关系定义参考子句,然后从所有受影响的分区或子表中可以看到行的前后图像。在继承子项的情况下,行图像仅包括触发器附加到的表中存在的列。

目前,不能在分区或继承子表上定义具有转换关系的行级触发器。此外,分区表上的触发器可能不是代替.

目前,或更换约束触发器不支持选项。

不建议在已对触发器表执行更新操作的事务中替换现有触发器。已经做出的触发触发决定或触发决定的一部分将不会被重新考虑,因此效果可能会令人惊讶。

有一些内置的触发函数可以用来解决常见问题,而无需编写自己的触发代码;看第 9.28 节.

# 例子

执行函数check_account_update每当桌子的一行帐户即将更新:

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

修改该触发器定义以仅在列中执行该函数平衡被指定为目标更新命令:

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

此表单仅执行函数 if 列平衡实际上已经改变了价值:

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

调用函数来记录更新帐户,但前提是发生了一些变化:

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为每个声明确认转移行偏移为零:

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

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

# 兼容性

创建触发器PostgreSQL 中的语句实现了 SQL 标准的一个子集。目前缺少以下功能:

  • 而转换表名称为触发器是使用指定的参考以标准方式从句中使用的行变量每一行触发器不能在一个参考条款。它们的可用方式取决于编写触发函数的语言,但对于任何一种语言都是固定的。一些语言有效地表现得好像有一个参考子句包含旧行如旧 新行如新.

  • 该标准允许将转换表与特定列一起使用更新触发器,但是应该在转换表中可见的行集取决于触发器的列列表。PostgreSQL 目前没有实现这一点。

  • PostgreSQL 只允许为触发的动作执行用户定义的函数。该标准允许执行许多其他 SQL 命令,例如创建表,作为触发的动作。通过创建执行所需命令的用户定义函数来解决此限制并不难。

    SQL 指定应按创建时间顺序触发多个触发器。PostgreSQL使用名称顺序,被判断为更方便。

    SQL 指定删除前级联删除触发级联的删除完成。PostgreSQL 行为适用于删除前始终在删除操作之前触发,甚至是级联操作。这被认为更一致。如果还存在非标准行为触发器在由引用操作引起的更新期间修改行或阻止更新。这可能导致违反约束或存储的数据不遵守引用约束。

    能够为单个触发器指定多个操作,使用或者是 SQL 标准的 PostgreSQL 扩展。

    触发触发器的能力截短是 SQL 标准的 PostgreSQL 扩展,它具有在视图上定义语句级触发器的能力。

创建约束触发器是 SQL 标准的 PostgreSQL 扩展。也是如此或更换选项。

# 也可以看看

改变触发器,下降触发器,创建函数,设置约束