36.1. 触发器行为概述

一个触发器是一种声明,告诉数据库应该在执行特定的操作的时候执行特定的函数。 触发器可以定义在一个INSERT, UPDATE, DELETE命令之前或者之后执行,要么是 对每行执行一次,要么是对每条 SQL语句执行一次。 如果发生触发器事件,那么将在合适的时刻调用触发器函数以处理该事件。

触发器函数必须在创建触发器之前,作为一个没有参数并且返回trigger类型的函数定义。 触发器函数通过特殊的TriggerData结构接收其输入,而不是用普通的函数参数方式。

一旦创建了一个合适的触发器函数,就可以用CREATE TRIGGER创建触发器。 同一个触发器函数可以用于多个触发器。

PostgreSQL提供按行与按语句触发的触发器。按行触发的触发器函数为触发语句影响的 每一行执行一次;按语句触发的触发器函数为每条触发语句执行一次,而不管影响的行数。 特别是,一个影响零行的语句将仍然导致按语句触发的触发器执行。 这两种类型的触发器有时候分别叫做行级触发器和语句级触发器。

触发器还通常分成before触发器和after triggers触发器。语句级别 的"before"触发器通常在语句开始做任何事情之前触发,而语句级别的"after"触发器 在语句结束时触发。行级别的"before"触发器在对特定行进行操作之前触发, 而行级别的"after"触发器 在语句结束的时候触发(但是在任何语句级别的"after"触发器之前)。

按语句触发的触发器应该总是返回NULL。 如果必要,按行触发的触发器函数可以给调用它的执行者返回一行 数据(一个类型为HeapTuple的数值), 那些在操作之前触发的触发器有以下选择:

一个无意导致任何这类行为的在操作之前触发的行级触发器必须仔细返回那个被 当作新行传进来的行。也就是说,对于INSERTUPDATE触发器而言, 是NEW行,对于DELETE触发器而言,是OLD行。

对于在操作之后触发的行级触发器,其返回值会被忽略,因此可以返回NULL

如果多于一个触发器为同样的事件定义在同样的关系上,触发器将按照名字的字母顺序触发。 如果是事件之前触发的触发器,每个触发器返回的可能已经被修改过的行成为下一个 触发器的输入。如果任何事件之前触发的触发器返回NULL, 那么对该行的操作将被丢弃并且随后的触发器也不会被触发。

一个触发器定义也可以声明一个布尔型的WHEN条件,用于检查触发器是否应该被触发。 在行级别触发器上,WHEN条件可以检查旧和/或新的列值。语句级的触发器 也可以有WHEN条件,尽管对其没有用。在一个before触发器中,WHEN条件 只在函数正在或将被执行之前被触发执行,因此使用WHEN条件实际上与在触发器开始时 执行相同条件的结果时一样的。然而,在一个after触发器中,WHEN条件只有在发生更新行时 才会执行,并且决定在语句结束之后,一个事件是否需要等待触发触发器。 因此当一个after触发器的WHEN条件没有返回真时,队列中的时间不需要在 语句结束后重新读取行。如果触发器只会被几些行触发时,在修改许多行的语句中会明显提高速度。

通常,行的 before 触发器用于检查或修改将要插入或者更新的数据。比如, 一个 before 触发器可以用于把当前时间插入一个timestamp column字段,或者跟踪该行的 两个元素是一致的。行的 after 触发器多数用于填充或者更新其它表,或者对其它表 进行一致性检查。这么区分工作的原因是 after 触发器肯定可以看到该行的最后数值, 而 before 触发器不能;还可能有其它的 before 触发器在其后触发。如果你没有具体的 原因定义触发器是 before 还是 after , 那么 before 触发器的效率高些,因为操作相关的信息不必保存到语句的结尾。

如果一个触发器函数执行 SQL 命令,而这些命令再次触发触发器,这就是所谓的级联触发器。 对级联触发器的级联深度没有明确的限制。有可能出现级联触发器导致同一个触发器递归调用的情况; 比如,一个INSERT触发器可能执行一个命令,把一个额外的行插入同一个表中, 导致INSERT触发器再次触发。避免这样无穷递归的问题是触发器程序员的责任。

在定义一个触发器的时候,可以声明一些参数。在触发器定义中包含参数的目的是 允许类似需求的不同触发器调用同一个函数。比如,可能有一个通用的触发器函数, 接受两个字段名字,把当前用户放在第一个,而当前时间戳在第二个。只要写得恰当, 那么这个触发器函数就可以和触发它的特定表无关。这样同一个函数就可以用于有着合适 字段的任何表的INSERT事件,实现自动跟踪交易表中的记录创建之类的问题。 如果定义成一个UPDATE触发器,还可以用它跟踪最后更新的事件。

每种支持触发器的编程语言都有自己的方法让触发器函数得到输入数据。 这些输入数据包括触发器事件的类型(比如 INSERTUPDATE)以及所有在 CREATE TRIGGER里面列出的参数。对于低层次的触发器,输入数据也包括INSERTUPDATE触发器的NEW和/或INSERTUPDATE 触发器的OLD行。 语句级别的触发器目前没有任何方法检查该语句修改的独立行。