合 PG中的触发器
简介
PostgreSQL 触发器是数据库的回调函数,它会在指定的数据库事件发生时自动执行/调用。
下面是关于 PostgreSQL 触发器几个比较重要的点:
- PostgreSQL 触发器可以在下面几种情况下触发:
- 在执行操作之前(在检查约束并尝试插入、更新或删除之前)。
- 在执行操作之后(在检查约束并插入、更新或删除完成之后)。
- 更新操作(在对一个视图进行插入、更新、删除时)。
- 触发器的 FOR EACH ROW 属性是可选的,如果选中,当操作修改时每行调用一次;相反,选中 FOR EACH STATEMENT,不管修改了多少行,每个语句标记的触发器执行一次。
- WHEN 子句和触发器操作在引用 NEW.column-name 和 OLD.column-name 表单插入、删除或更新时可以访问每一行元素。其中 column-name 是与触发器关联的表中的列的名称。
- 如果存在 WHEN 子句,PostgreSQL 语句只会执行 WHEN 子句成立的那一行,如果没有 WHEN 子句,PostgreSQL 语句会在每一行执行。
- BEFORE 或 AFTER 关键字决定何时执行触发器动作,决定是在关联行的插入、修改或删除之前或者之后执行触发器动作。
- 要修改的表必须存在于同一数据库中,作为触发器被附加的表或视图,且必须只使用 tablename,而不是 database.tablename。
- 当创建约束触发器时会指定约束选项。这与常规触发器相同,只是可以使用这种约束来调整触发器触发的时间。当约束触发器实现的约束被违反时,它将抛出异常。
创建触发器语法
创建触发器时的基础语法如下:
1 2 3 4 5 | CREATE TRIGGER trigger_name [BEFORE|AFTER|INSTEAD OF] event_name ON table_name [ -- 触发器逻辑.... ]; |
在这里,event_name 可以是在所提到的表 table_name 上的 INSERT、DELETE 和 UPDATE 数据库操作。您可以在表名后选择指定 FOR EACH ROW。
以下是在 UPDATE 操作上在表的一个或多个指定列上创建触发器的语法:
1 2 3 4 5 | CREATE TRIGGER trigger_name [BEFORE|AFTER] UPDATE OF column_name ON table_name [ -- 触发器逻辑.... ]; |
实例
让我们假设一个情况,我们要为被插入到新创建的 COMPANY 表(如果已经存在,则删除重新创建)中的每一个记录保持审计试验:
1 2 3 4 5 6 7 | lhrpgdb=# CREATE TABLE COMPANY( ID INT PRIMARY KEY NOT NULL, NAME TEXT NOT NULL, AGE INT NOT NULL, ADDRESS CHAR(50), SALARY REAL ); |
为了保持审计试验,我们将创建一个名为 AUDIT 的新表。每当 COMPANY 表中有一个新的记录项时,日志消息将被插入其中:
1 2 3 4 | lhrpgdb=# CREATE TABLE AUDIT( EMP_ID INT NOT NULL, ENTRY_DATE TEXT NOT NULL ); |
在这里,ID 是 AUDIT 记录的 ID,EMP_ID 是来自 COMPANY 表的 ID,DATE 将保持 COMPANY 中记录被创建时的时间戳。所以,现在让我们在 COMPANY 表上创建一个触发器,如下所示:
1 | lhrpgdb=# CREATE TRIGGER example_trigger AFTER INSERT ON COMPANY FOR EACH ROW EXECUTE PROCEDURE auditlogfunc(); |
auditlogfunc() 是 PostgreSQL 一个程序,其定义如下:
1 2 3 4 5 6 | CREATE OR REPLACE FUNCTION auditlogfunc() RETURNS TRIGGER AS $example_table$ BEGIN INSERT INTO AUDIT(EMP_ID, ENTRY_DATE) VALUES (new.ID, current_timestamp); RETURN NEW; END; $example_table$ LANGUAGE plpgsql; |
现在,我们开始往 COMPANY 表中插入数据:
1 | lhrpgdb=# INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (1, 'Paul', 32, 'California', 20000.00 ); |
这时,COMPANY 表中插入了一条记录:
同时, AUDIT 表中也插入了一条记录,因为我们在插入 COMPANY 表时创建了一个触发器。相似的,我们也可以根据需求在更新和删除时创建触发器:
1 2 3 4 | emp_id | entry_date --------+--------------------------------- 1 | 2013-05-05 15:49:59.968+05:30 (1 row) |
CREATE TRIGGER — 定义一个新触发器
大纲
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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
触发器被执行)。
被指定为要触发INSTEAD OF
触发器事件的触发器必须被标记为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
触发器的问题提供可行的解决方案。
关于触发器的更多信息请见第 38 章。