七月 29, 2023
摘要:触发器是存储在数据库中并在发生指定事件时触发的过程。导致触发器运行的关联事件可以绑定到特定的数据库表、数据库视图、数据库模式或数据库本身。
Oracle 用法
触发器可以在以下之后运行:
- 数据操作语言(DML)语句,如
DELETE
、INSERT
或UPDATE
。 - 数据定义语言(DDL)语句,如
CREATE
、ALTER
或DROP
。 - 数据库事件和操作,如
SERVERERROR
、LOGON
、LOGOFF
、STARTUP
或SHUTDOWN
。
触发器类型
- DML 触发器可以在表或视图上创建,并在插入、更新或删除数据时触发。触发器可以在 DML 命令运行之前或之后触发。
- 可以在不可编辑的视图上创建 INSTEAD OF 触发器。
INSTEAD OF
触发器提供了一种应用程序透明的方法,用于修改无法由 DML 语句修改的视图。 - SYSTEM 事件触发器在数据库或架构级别定义,包括在特定事件后触发的触发器:
- 用户登录和注销。
- 数据库事件(启动/关闭)、DataGuard 事件、服务器错误。
例子
创建一个触发器,该触发器在从表PROJECTS
中删除行后运行,或者在项目的主键更新时运行。
CREATE OR REPLACE TRIGGER PROJECTS_SET_NULL
AFTER DELETE OR UPDATE OF PROJECTNO ON PROJECTS
FOR EACH ROW
BEGIN
IF UPDATING AND :OLD.PROJECTNO != :NEW.PROJECTNO OR DELETING THEN
UPDATE EMP SET EMP.PROJECTNO = NULL
WHERE EMP.PROJECTNO = :OLD.PROJECTNO;
END IF;
END;
/
Trigger created.
DELETE FROM PROJECTS WHERE PROJECTNO=123;
SELECT PROJECTNO FROM EMP WHERE PROJECTNO=123;
PROJECTNO
NULL
在表上创建系统/模式触发器。如果为HR
模式中的对象运行DROP
类型的 DDL 命令,则触发器将触发。它可以防止丢弃对象并引发应用程序错误。
CREATE OR REPLACE TRIGGER PREVENT_DROP_TRIGGER
BEFORE DROP ON HR.SCHEMA
BEGIN
RAISE_APPLICATION_ERROR (num => -20000,
msg => 'Cannot drop object');
END;
/
Trigger created.
DROP TABLE HR.EMP
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-20000: Cannot drop object
ORA-06512: at line 2
有关详细信息,请参阅 Oracle 文档中的 CREATE TRIGGER 语句。
PostgreSQL 用法
触发器是存储在数据库中并在发生指定事件时触发的过程。PostgreSQL 中的 DML 触发器共享 Oracle 触发器中存在的许多功能。
- DML 触发器(基于与表相关的事件(如 DML)触发的触发器)。
- 事件触发器(在某些数据库事件(如运行 DDL 命令)之后触发的触发器)。
与 Oracle 触发器不同,PostgreSQL 触发器必须调用函数,并且不支持将 PL/pgSQL 代码的匿名块作为触发器主体的一部分。用户提供的函数声明时不带参数,并且返回类型为触发器。
PostgreSQL 创建触发器概要
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 PROCEDURE function_name ( arguments )
where event can be one of:
INSERT
UPDATE [ OF column_name [, ... ] ]
DELETE
TRUNCATE
注意
REFERENCING
是 PostgreSQL 10 中引入的新选项。可以将此选项与AFTER
触发器一起使用,以与OLD
或NEW TABLE
更改行的整体视图进行交互。
在某些情况下,可以多次触发多个触发器。这包括未计划运行的触发器,例如:
- 带有
ON CONFLICT DO UPDATE
子句的INSERT
语句可能会导致插入和更新操作触发。 - 由外键强制导致
UPDATE
或DELETE
可以触发触发器。例如,ON UPDATE CASCADE
或者ON DELETE SET NULL
可以触发应该由表上的UPDATE
或DELETE
命令触发的触发器。
PostgreSQL DML 触发器
PostgreSQL触发器可以在 DML 操作之前或之后运行。
- 在尝试操作一行数据之前触发。
- 在约束检查之前,并尝试
INSERT
、UPDATE
或DELETE
时。 - 如果触发器在事件之前或代替事件触发,则触发器可以跳过当前行的操作或更改要插入的行(仅限
INSERT
和UPDATE
操作)。
- 在约束检查之前,并尝试
- 操作完成后,在约束检查完并完成
INSERT
、UPDATE
或DELETE
命令之后。如果触发器在事件发生后触发,则触发器可以看到所有更改,包括其他触发器的效果。
PostgreSQL 触发器可以在视图上创建时运行INSTEAD OF
形式的 DML 命令。
PostgreSQL 触发器可以针对受 DML 语句影响的每行记录(FOR EACH ROW
)运行,也可以作为 DML 语句的一部分只运行一次(FOR EACH STATEMENT
)。
触发时间 | 数据库事件 | 行级触发器 | 语句级触发器 |
---|---|---|---|
之前 | INSERT, UPDATE, DELETE | 表和外表 | 表、视图和外部表 |
之前 | TRUNCATE | 不适用 | 表 |
之后 | INSERT, UPDATE, DELETE | 表和外表 | 表、视图和外部表 |
之后 | TRUNCATE | 不适用 | 表 |
替代执行 | INSERT, UPDATE, DELETE | 视图 | 不适用 |
替代执行 | TRUNCATE | 不适用 | 不适用 |
PostgreSQL 事件触发器
当数据库中发生与触发器关联的特定事件时,将运行事件触发器。支持的事件包括:ddl_command_start
、ddl_command_end
、table_rewrite
和sql_drop
。
ddl_command_start
在运行CREATE
、ALTER
、DROP
、SECURITY LABEL
、COMMENT
、GRANT
、REVOKE
或SELECT INTO
命令之前发生。ddl_command_end
在命令完成之后和事务提交之前发生。sql_drop
仅针对 DROP 类型的 DDL 命令触发。在ddl_command_end
前触发。
有关详细信息,请参阅 PostgreSQL 文档中的事件触发器触发矩阵。
例子
创建 DML 触发器。要在 PostgreSQL 中创建 Oracle DML 触发器的等效版本,请首先创建一个函数触发器,该触发器将存储触发器的运行逻辑。
CREATE OR REPLACE FUNCTION PROJECTS_SET_NULL()
RETURNS TRIGGER
AS $$
BEGIN
IF TG_OP = 'UPDATE' AND OLD.PROJECTNO != NEW.PROJECTNO OR
TG_OP = 'DELETE' THEN
UPDATE EMP
SET PROJECTNO = NULL
WHERE EMP.PROJECTNO = OLD.PROJECTNO;
END IF;
IF TG_OP = 'UPDATE' THEN RETURN NULL;
ELSIF TG_OP = 'DELETE' THEN RETURN NULL;
END IF;
END;
$$ LANGUAGE PLPGSQL;
创建触发器。
CREATE TRIGGER TRG_PROJECTS_SET_NULL
AFTER UPDATE OF PROJECTNO OR DELETE
ON PROJECTS
FOR EACH ROW
EXECUTE PROCEDURE PROJECTS_SET_NULL();
通过从 PROJECTS 表中删除一行来测试触发器。
DELETE FROM PROJECTS WHERE PROJECTNO=123;
SELECT PROJECTNO FROM EMP WHERE PROJECTNO=123;
projectno
(0 rows)
创建一个 DDL 触发器,该触发器是 Oracle DDL 系统/模式级别触发器的等效版本(例如阻止对 HR 模式中的对象运行 DDL DROP 的触发器)。
创建事件触发器函数。
请注意,创建触发器函数时不带参数,并且返回类型必须为TRIGGER
或EVENT_TRIGGER
。
CREATE OR REPLACE FUNCTION ABORT_DROP_COMMAND()
RETURNS EVENT_TRIGGER
AS $$
BEGIN
RAISE EXCEPTION 'The % Command is Disabled', tg_tag;
END;
$$ LANGUAGE PLPGSQL;
创建事件触发器,该触发器将在 DDL DROP 命令开始之前触发。
CREATE EVENT TRIGGER trg_abort_drop_command
ON DDL_COMMAND_START
WHEN TAG IN ('DROP TABLE', 'DROP VIEW',
'DROP FUNCTION', 'DROP SEQUENCE',
'DROP MATERIALIZED VIEW', 'DROP TYPE')
EXECUTE PROCEDURE abort_drop_command();
通过尝试删除 EMPLOYEE 表来测试触发器。
DROP TABLE EMPLOYEES;
ERROR: The DROP TABLE Command is Disabled
CONTEXT: PL/pgSQL function abort_drop_command() line 3 at RAISE
总结
触发 | Oracle | PostgreSQL |
---|---|---|
在更新触发器之前,行级别 | CREATE OR REPLACE TRIGGER check_update BEFORE UPDATE ON projects FOR EACH ROW BEGIN /*Trigger body*/ END; / |
CREATE TRIGGER check_update BEFORE UPDATE ON employees FOR EACH ROW EXECUTE PROCEDURE myproc(); |
在更新触发器之前,语句级别 | CREATE OR REPLACE TRIGGER check_update BEFORE UPDATE ON projects BEGIN /*Trigger body*/ END; / |
CREATE TRIGGER check_update BEFORE UPDATE ON employees FOR EACH STATEMENT EXECUTE PROCEDURE myproc(); |
系统/事件触发器 | CREATE OR REPLACE TRIGGER drop_trigger BEFORE DROP ON hr.SCHEMA BEGIN RAISE_APPLICATION_ERROR ( num => -20000, msg => 'Cannot drop object'); END; / |
CREATE EVENT TRIGGER trg_drops ON ddl_command_start EXECUTE PROCEDURE trg_drops(); |
在触发器中引用:old 和:new |
在触发器主体中使用 “:NEW” 和 “:OLD”:CREATE OR REPLACE TRIGGER Upper-NewDeleteOld BEFORE INSERT OR UPDATE OF first_name ON employees FOR EACH ROW BEGIN :NEW.first_name := UPPER(:NEW.first_name); :NEW.salary := :OLD.salary; END; / |
在触发器过程主体中使用 “NEW“ 和 ”OLD“:CREATE OR REPLACE FUNCTION log_emp_name_upd() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF NEW.last_name <> OLD.last_name THEN INSERT INTO employee_audit (employee_ id,last_name,changed_on) VALUES (OLD.id,OLD.last_name,now()); END IF; RETURN NEW; END; $$ CREATE TRIGGER last_name_change_trg BEFORE UPDATE ON employees FOR EACH ROW EXECUTE PROCEDURE log_last_emp_name_upd(); |
数据库事件级别触发器 | CREATE TRIGGER register_shutdown ON DATABASE SHUTDOWN BEGIN Insert into logging values ('DB was shut down', sysdate); commit; END; / |
不适用 |
删除触发器 | DROP TRIGGER last_name_change_trg; |
DROP TRIGGER last_name_change_trg on employees; |
修改触发器运行的逻辑 | 可与创建或替换一起使用CREATE OR REPLACE TRIGGER UpperNewDeleteOld BEFORE INSERT OR UPDATE OF first_name ON employees FOR EACH ROW BEGIN <<NEW CONTENT>> END; / |
在触发器中调用的函数上使用创建或替换(触发器保持不变)CREATE or replace FUNCTION UpperNewDeleteOld() RETURNS trigger AS $UpperNewDeleteOld$ BEGIN <<NEW CONTENT>> END; $UpperNewDeleteOld$ LANGUAGE plpgsql; |
启用触发器 | ALTER TRIGGER UpperNewDeleteOld ENABLE; |
alter table employees enable trigger Upper-NewDeleteOld; |
禁用触发器 | ALTER TRIGGER UpperNewDeleteOld DISABLE; |
alter table employees disable trigger Upper-NewDeleteOld; |
有关详细信息,请参阅 PostgreSQL 文档中的 CREATE TRIGGER 和触发函数。