9.1第三十六章

From PostgreSQL wiki

(Difference between revisions)
Jump to: navigation, search
(触发器功能概述)
Line 9: Line 9:
  
 
== 触发器功能概述 ==
 
== 触发器功能概述 ==
 +
一个触发器是一种声明,告诉数据库应该在执行特定的操作的时候执行特定的函数。触发器可以作用于表和视图。
 +
 +
作用于表时,触发器可以定义在一个INSERT,UPDATE 或者 DELETE 命令之前或者之后执行,要么是每个记录行被修改,要么是每执行一次 SQL都会触发。UPDATE 触发器还可以设置为UPDATE语句中SET子句包含的特定列发生改动时触发。如果发生触发器事件,那么将在合适的时刻调用触发器函数以处理该事件。
 +
 +
作用于视图时,触发器不可以定义在 INSERT,UPDATE 或者 DELETE 操作上。而是定义在视图中的每一行需要修改时。触发器的任务是对下层的基础表做必要的修改并返回被修改的行到视图中。作用于视图的触发器还可以被定义为执行INSERT,UPDATE或DELETE操作之前或之后执行一条SQL语句。
 +
 +
触发器函数必须在创建触发器之前定义。触发器函数必须声明为一个没有参数并且返回trigger类型的函数。(触发器函数通过特殊的TriggerData结构接收其输入,而不是用普通函数参数那种形式。)
 +
 +
一旦创建了一个合适的触发器函数,触发器就可以用 CREATE TRIGGER 创建。同一个触发器函数可以用于多个触发器。
 +
 +
PostgreSQL 提供按行触发和按语句触发的触发器。在按行触发的触发器里,在每一行被影响时触发一次。相比之下,一个按语句触发的触发器是在每执行一次合适的语句执行一次的,而不管影响的行数。特别是,一个影响零行的语句将仍然导致任何适用的按语句触发的触发器的执行。这两种类型的触发器有时候分别叫做行级别的触发器和语句级别的触发器。作用于TRUNCATE的触发器只能是语句级别的。对于视图而言,在某个操作之前或之后触发的触发器只能定义为语句级别,但是除了INSERT,UPDATE 或 DELETE之外,只能定义为行级别。
 +
 +
触发器也可以根据触发时机分类,分别为 BEFORE 触发器、AFTER 触发器和 INSTEAD OF触发器。语句级别的 BEFORE 触发器通常在语句开始做任何事情之前触发,而语句级别的 AFTER 触发器在语句的最后触发。这种类型的触发器可以定义在表和视图上。行级别的 BEFORE 触发器在对特定行进行操作的时候马上触发,而行级别的 AFTER 触发器在语句结束的时候触发(但是在任何语句级别的 AFTER 触发器之前)。这类触发器只能定义在表上。行级别的 INSTEAD OF 触发器只能定义在视图上,并且一旦对视图数据操作时触发。
 +
 +
语句级别的触发器函数应该总是返回 NULL。如果必要,触发器函数可以给调用它的执行者返回表中的数据行(一个类型为 HeapTuple 的数值)。那些在操作之前触发的行级触发器有以下选择:
 +
 +
*它可以返回 NULL 以忽略对当前行的操作。这就指示执行器不要执行调用该触发器的行级别操作(对特定行的插入、修改或者删除)。
 +
 +
*仅对行级别 INSERT 和 UPDATE 触发器而言,其返回将要被插入和更新的行。这样就允许触发器函数修改被插入或者更新的行。
 +
 +
行级别 BEFORE 触发器并没有这些行为,它返回传进来行(也就是说,对 INSERT 和 UPDATE 触发器来说返回新行,对 DELETE 触发器来说返回被删除的行)。
 +
 +
行级别 INSTEAD OF 触发器要么返回 NULL 以表示其没修改某视图的底层表,要么返回传进来的视图行(INSERT 和 UPDATE操作的 NEW 行, DELETE操作的 OLD 行)。一个非空的返回值标志着触发器修改了视图的相应数据。这将导致该命令所影响数据行的计数增加。对于 INSERT 和 UPDATE 操作,触发器在返回前可能修改 NEW 行。这将改变被 INSERT RETURNING 或 UPDATE RETURNING 返回的数据,当视图没能展示其数据时非常有用。
 +
 +
对于在操作之后触发的行级别的触发器,其返回值会被忽略,因此他们可以返回NULL。
 +
 +
如果多于一个触发器为同样的事件定义在同样的关系上, 触发器将按照由名字的字母顺序排序的顺序触发。 如果是事件之前触发的触发器,每个触发器返回的可能已经被修改过的行成为下一个触发器的输入。 如果任何事件之前触发的触发器返回 NULL 指针, 那么对该行的操作将被丢弃并且随后的触发器不会被触发。
 +
 +
触发器的定义同样可以指定布尔值的 WHEN 条件,它将用来决定触发器是否应该被触发。行级触发器的 WHEN 条件可以检查该行某列的旧值和新值。(语句级触发器也可以有 WHEN 条件,尽管这个功能没那么有用。)对于BEFORE触发器,WHEN条件在函数执行前判定,所以使用WHEN条件与在函数中判定没有区别。然而,对于AFTER触发器,WHEN条件在行被更新后判定,它决定该事件是否加入被触发队列。所以,如果AFTER触发器WHEN条件没有返回真值,也就没有必要再将该事件放到触发队列,也没必要在语句结束后重新取得行。如果该触发器只定义在少量的行,这个功能对于修改大量行时就行有意义,将大大提升修改速度。INSTEAD OF触发器不支持WHEN条件。
 +
 +
通常,行级 before 触发器用于检查或修改将要插入或者更新的数据。 比如,一个 before 触发器可以用于把当前时间插入一个 timestamp 字段, 或者跟踪该行的两个元素是否一致。行的 after 触发器多数用于填充或者更新其它表, 或者对其它表进行一致性检查。这么分工的原因是, after 触发器肯定可以看到该行的最后数值, 而 before 触发器不能;还可能有其它的 before 触发器在其后触发。 如果你没有具体的原因定义触发器是 before 还是 after,那么 before 触发器的效率高些, 因为操作相关的信息不必保存到语句的结尾。
 +
 +
如果一个触发器函数执行 SQL 命令,然后这些命令可能再次触发触发器。 这就是所谓的级联触发器。对级联触发器的级联深度没有明确的限制。 有可能出现级联触发器导致同一个触发器的递归调用的情况; 比如,一个 INSERT 触发器可能执行一个命令, 把一个额外的行插入同一个表中,导致 INSERT 触发器再次激发。 避免这样的无穷递归的问题是触发器程序员的责任。
 +
 +
在定义一个触发器的时候,我们可以声明一些参数。在触发器定义里面包含参数的目的是允许类似需求的不同触发器调用同一个函数。 比如,我们可能有一个通用的触发器函数,接受两个字段名字,把当前用户放在第一个,而当前时间戳在第二个。 只要我们写得恰当,那么这个触发器函数就可以和触发它的特定表无关。这样同一个函数就可以用于有着合适字段的任何表的 INSERT 事件,实现自动跟踪交易表中的记录创建之类的问题。如果定义成一个 UPDATE 触发器,我们还可以用它跟踪最后更新的事件。
 +
 +
每种支持触发器的编程语言都有自己的方法让触发器函数得到输入数据。这些输入数据包括触发器事件的类型(比如,INSERT 或者 UPDATE)以及所有在 CREATE TRIGGER 里面列出的参数。对于行级触发器,输入数据也包括 INSERT 和 UPDATE 触发器的 NEW 行,和/或 UPDATE 和 DELETE 触发器的 OLD 行。语句级别的触发器目前没有任何方法检查改语句所修改行。
 +
 
== 数据更新的可见性 ==
 
== 数据更新的可见性 ==
 
== 用C语言写触发器 ==
 
== 用C语言写触发器 ==

Revision as of 04:11, 20 September 2013

第36章 触发器


这章概括了关于触发器编写的内容。触发器可以用大多数过程语言编写,包括PL/pgSQL (第39章), PL/Tcl (第40章), PL/Perl (第41章), 还有 PL/Python (第42章)。 阅读完本章以后,你可以参照你最喜欢的语言相关的章节,找出用该语言编写触发器的相关细节。 尽管大多数人感觉用过程语言编写触发器相对容易些,但是用C语言同样也可以写出触发器来。 目前还是不能用单纯的SQL语句编写触发器函数。

Contents

触发器功能概述

一个触发器是一种声明,告诉数据库应该在执行特定的操作的时候执行特定的函数。触发器可以作用于表和视图。

作用于表时,触发器可以定义在一个INSERT,UPDATE 或者 DELETE 命令之前或者之后执行,要么是每个记录行被修改,要么是每执行一次 SQL都会触发。UPDATE 触发器还可以设置为UPDATE语句中SET子句包含的特定列发生改动时触发。如果发生触发器事件,那么将在合适的时刻调用触发器函数以处理该事件。

作用于视图时,触发器不可以定义在 INSERT,UPDATE 或者 DELETE 操作上。而是定义在视图中的每一行需要修改时。触发器的任务是对下层的基础表做必要的修改并返回被修改的行到视图中。作用于视图的触发器还可以被定义为执行INSERT,UPDATE或DELETE操作之前或之后执行一条SQL语句。

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

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

PostgreSQL 提供按行触发和按语句触发的触发器。在按行触发的触发器里,在每一行被影响时触发一次。相比之下,一个按语句触发的触发器是在每执行一次合适的语句执行一次的,而不管影响的行数。特别是,一个影响零行的语句将仍然导致任何适用的按语句触发的触发器的执行。这两种类型的触发器有时候分别叫做行级别的触发器和语句级别的触发器。作用于TRUNCATE的触发器只能是语句级别的。对于视图而言,在某个操作之前或之后触发的触发器只能定义为语句级别,但是除了INSERT,UPDATE 或 DELETE之外,只能定义为行级别。

触发器也可以根据触发时机分类,分别为 BEFORE 触发器、AFTER 触发器和 INSTEAD OF触发器。语句级别的 BEFORE 触发器通常在语句开始做任何事情之前触发,而语句级别的 AFTER 触发器在语句的最后触发。这种类型的触发器可以定义在表和视图上。行级别的 BEFORE 触发器在对特定行进行操作的时候马上触发,而行级别的 AFTER 触发器在语句结束的时候触发(但是在任何语句级别的 AFTER 触发器之前)。这类触发器只能定义在表上。行级别的 INSTEAD OF 触发器只能定义在视图上,并且一旦对视图数据操作时触发。

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

  • 它可以返回 NULL 以忽略对当前行的操作。这就指示执行器不要执行调用该触发器的行级别操作(对特定行的插入、修改或者删除)。
  • 仅对行级别 INSERT 和 UPDATE 触发器而言,其返回将要被插入和更新的行。这样就允许触发器函数修改被插入或者更新的行。

行级别 BEFORE 触发器并没有这些行为,它返回传进来行(也就是说,对 INSERT 和 UPDATE 触发器来说返回新行,对 DELETE 触发器来说返回被删除的行)。

行级别 INSTEAD OF 触发器要么返回 NULL 以表示其没修改某视图的底层表,要么返回传进来的视图行(INSERT 和 UPDATE操作的 NEW 行, DELETE操作的 OLD 行)。一个非空的返回值标志着触发器修改了视图的相应数据。这将导致该命令所影响数据行的计数增加。对于 INSERT 和 UPDATE 操作,触发器在返回前可能修改 NEW 行。这将改变被 INSERT RETURNING 或 UPDATE RETURNING 返回的数据,当视图没能展示其数据时非常有用。

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

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

触发器的定义同样可以指定布尔值的 WHEN 条件,它将用来决定触发器是否应该被触发。行级触发器的 WHEN 条件可以检查该行某列的旧值和新值。(语句级触发器也可以有 WHEN 条件,尽管这个功能没那么有用。)对于BEFORE触发器,WHEN条件在函数执行前判定,所以使用WHEN条件与在函数中判定没有区别。然而,对于AFTER触发器,WHEN条件在行被更新后判定,它决定该事件是否加入被触发队列。所以,如果AFTER触发器WHEN条件没有返回真值,也就没有必要再将该事件放到触发队列,也没必要在语句结束后重新取得行。如果该触发器只定义在少量的行,这个功能对于修改大量行时就行有意义,将大大提升修改速度。INSTEAD OF触发器不支持WHEN条件。

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

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

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

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

数据更新的可见性

用C语言写触发器

一个完整的触发器示例

Personal tools