CREATE TRIGGER
CREATE TRIGGER - 定义一个新的触发器
语法格式
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 )
这里的event可以是下列之一:
INSERT
UPDATE [ OF column_name [, ... ] ]
DELETE
TRUNCATE
说明
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 触发器)。
指定用于触发事件的触发器必须标记为 FOR EACH ROW,并且只能在视图上定义。视图上的 BEFORE 和 AFTER 触发器必须标记为 FOR EACH STATEMENT 。
此外,触发器可以定义为 TRUNCATE 触发,但只有 FOR EACH STATEMENT 。
下表总结了可以在表,视图和外表上使用哪些类型的触发器:
当 | 事件 | 行级 | 语句级别 |
BEFORE | INSERT / UPDATE / DELETE | 表和外表 | 表,视图和外表 |
TRUNCATE | - | 表 | |
AFTER | INSERT / UPDATE / DELETE | 表和外表 | 表,视图和外表 |
TRUNCATE | - | 表 | |
INSTEAD OF | INSERT / UPDATE / DELETE | 查看 | - |
TRUNCATE | - | - |
还有,一个触发器定义可以指定一个布尔的WHEN条件,它将被测试来看看该触发器是否应该被触发。在行级触发器中,WHEN条件可以检查该行的列的新旧值。语句级触发器也可以有WHEN条件,尽管该特性对于它们不是很有用(因为条件不能引用表中的任何值)。
如果有多个同种触发器被定义为相同事件触发,它们将按照名称的字母表顺序被触发。
当CONSTRAINT选项被指定,这个命令会创建一个约束触发器。这和一个常规触发器相同,不过触发该触发器的时机可以使用SET CONSTRAINTS调整。约束触发器必须是表上的AFTER ROW触发器。它们可以在导致触发器事件的语句末尾被引发或者在包含该语句的事务末尾被引发。在后一种情况中,它们被称作是被延迟。一个待处理的延迟触发器的引发也可以使用SET CONSTRAINTS立即强制发生。当约束触发器实现的约束被违背时,约束触发器应该抛出一个异常。
REFERENCING选项启用对传递关系的收集,传递关系是包括被当前SQL语句插入、删除或者修改的行的行集合。这个特性让触发器能看到该语句做的事情的全局视图,而不是一次只看到一行。仅对非约束触发器的AFTER触发器允许这个选项。此外,如果触发器是一个UPDATE触发器,则它不能指定column_name列表。OLD TABLE仅可以被指定一次,并且只能为在UPDATE或DELETE事件上引发的触发器指定,它创建的传递关系包含有该语句更新或删除的所有行的前映像。类似地,NEW TABLE仅可以被指定一次,并且只能为在UPDATE或INSERT事件上引发的触发器指定,它创建的传递关系包含有该语句更新或插入的所有行的后映像。
SELECT不修改任何行,因此你无法创建SELECT触发器。规则和视图可以为需要SELECT触发器的问题提供可行的解决方案。
参数说明
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。
condition
一个决定该触发器函数是否将被实际执行的布尔表达式。如果指定了WHEN,只有condition返回true时才会调用该函数。在FOR EACH ROW触发器中,WHEN条件可以分别写OLD.column_name或者NEW.column_name来引用列的新旧行值。当然,INSERT触发器不能引用OLD并且DELETE触发器不能引用NEW。 INSTEAD OF触发器不支持WHEN条件。 当前,WHEN表达式不能包含子查询。 注意对于约束触发器,对于WHEN条件的计算不会被延迟,而是直接在行更新操作被执行之后立刻发生。如果该条件计算得不到真,那么该触发器就不会被放在延迟执行的队列中。
function_name
一个用户提供的函数,它被声明为不用参数并且返回类型trigger,当触发器引发时会执行该函数。 在CREATE TRIGGER的语法中,关键词FUNCTION和PROCEDURE是等效的,但是任何情况下被引用的函数必须是一个函数而不是过程。这里,关键词PROCEDURE的使用是有历史原因的并且已经被废弃。
arguments
一个可选的逗号分隔的参数列表,它在该触发器被执行时会被提供给该函数。参数是字符串常量。简单的名称和数字常量也可以被写在这里,但是它们将全部被转换成字符串。请检查该触发器函数的实现语言的描述来找出在函数内部如何访问这些参数,这可能与普通函数参数不同。
示例
每当表 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();