触发器
与存储过程类似,触发器是一个命名的 PL/iSQL 单元,存储在数据库中,可以重复调用。但是与存储过程不同的是,我们可以启用和禁用触发器,但不支持显示调用。触发器启用后,数据库会在触发事件发生时自动调用它,即触发器触发。触发器禁用后,触发器不会触发。
用户可通过使用 CREATE TRIGGER 语句创建触发器。可以根据触发语句及其作用的对象指定触发事件。触发器是在对象(可以是表、视图、模式或数据库)上创建或定义的。还可以指定时间点,该时间点决定触发器是在触发语句运行之前还是之后触发,以及是否针对触发语句影响的每一行触发。默认情况下,触发器创建时处于启用状态。
按照触发事件,触发器可以分为DML触发器和系统触发器:
如果在表或视图上创建触发器,触发事件由 DML 语句组成,这种触发器称为 DML 触发器。DML 触发器可以是简单的DML触发器,也可以是复合的DML触发器。
如果触发器是在模式或数据库上创建的,触发事件由 DDL 或数据库操作语句组成,这种触发器称为系统触发器 。
用户可通过ALL_TRIGGERS、DBA_TRIGGERS和USER_TRIGGERS视图查看创建的触发器信息。
说明:
该功能在V9.0.5及以后的版本中支持。
兼容Oracle模式下,支持原生PG的触发器定义和使用,但创建触发器要以 ‘/‘为结束符。
目前暂不支持复合的DML触发器和系统触发器。
目前暂不支持触发器谓词、使用动态SQL创建兼容Oracle的触发器。
说明
create_trigger
{width=”5.7659722222222225in” height=”0.71875in”}
plsql_trigger_source
{width=”5.764583333333333in”
height=”0.7590277777777777in”}
sharing_clause(仅语法支持)
{width=”2.7930555555555556in”
height=”0.5611111111111111in”}
default_collation_clause(仅语法支持)
{width=”2.5430555555555556in”
height=”0.30694444444444446in”}
simple_dml_trigger
{width=”6.543055555555555in”
height=”0.3472222222222222in”}
instead_of_dml_trigger
{width=”6.736111111111111in”
height=”0.5020833333333333in”}
referencing_clause
{width=”4.299305555555556in”
height=”1.3333333333333333in”}
trigger_edition_clause(仅语法支持)
{width=”2.75in”
height=”0.9479166666666666in”}
trigger_ordering_clause
{width=”4.072916666666667in”
height=”0.9166666666666666in”}
trigger_body
{width=”2.5729166666666665in”
height=”0.78125in”}
alter_trigger
{width=”5.763194444444444in”
height=”1.3618055555555555in”}
trigger_compile_clause
{width=”5.763194444444444in”
height=”0.6201388888888889in”}
drop_trigger
{width=”4.604166666666667in”
height=”0.6666666666666666in”}
限制:
EDITIONABLE / NONEDITIONABLE (仅语法兼容)
FORWARD/REVERSE (仅语法兼容)
sharing_clause、default_collation_clause (仅语法兼容)
HGDB V9暂不支持[compound_dml_trigger和]{.mark}system_trigger
HGDB V9暂不支持触发器谓词、使用动态SQL创建兼容Oracle的触发器。
CREATE TRIGGER
- OR REPLACE
如果触发器存在,则重新创建它,并重新编译它。
- [ EDITIONABLE | NONEDITIONABLE ]
仅语法支持,默认是EDITIONABLE。
plsql_trigger_source
- schema
模式名
- trigger_name
要创建的触发器名称。同一schema中的触发器不能同名。触发器可以与其他对象同名(例如,表和触发器可以同名)。但为了避免混淆,不建议这样做。
simple_dml_trigger
介绍
使用simple_dml_trigger子句可以创建一个简单的DML触发器。
简单的 DML 触发器只会在以下某个时间点触发:
在触发语句运行之前(该触发器称为语句级 BEFORE 触发器。)
在触发语句运行之后(该触发器称为语句级 AFTER 触发器。)
在触发语句影响的每一行之前(该触发器称为行级 BEFORE 触发器。)
在触发语句影响的每一行之后(该触发器称为行级 AFTER 触发器。)
语法说明
- BEFORE
使数据库在运行触发事件之前触发触发器。对于行触发器,触发器在每个受影响的行发生更改之前触发。
在 BEFORE 语句级触发器中,触发器主体无法读取 :NEW 或 :OLD。(在BEFORE 行触发器中,触发器主体可以读取和写入 :OLD 和 :NEW 字段。)
- AFTER
运行触发事件后,导致数据库触发触发器。对于行触发器,触发器在每个受影响的行发生更改后触发。
在 AFTER 语句级触发器中,触发器主体无法读取 :NEW 或 :OLD。(在AFTER 行触发器中,触发器主体可以读取但不能写入 :OLD 和 :NEW 字段。)
- FOR EACH ROW
将触发器创建为行级触发器。数据库会为受触发语句影响且满足 WHEN 条件中定义的可选触发器约束的每一行触发一个行触发器。
如果省略此子句,则触发器为语句触发器。仅当触发语句满足可选触发器约束时,数据库才会触发语句触发器。
- [ ENABLE | DISABLE ]
创建处于启用(默认)或禁用状态的触发器。创建处于禁用状态的触发器可让您确保在启用触发器之前,该触发器的编译不会出现错误。
- WHEN (condition)
指定数据库针对触发语句影响的每一行的 SQL 条件。如果受影响行的条件值为 TRUE,则为该行运行 trigger_body;否则,不会为该行运行 trigger_body。无论条件值如何,触发语句都会运行。
condition中可以包含referencing_clause子句。
如果指定此子句,则还必须指定 FOR EACH ROW。
condition不能包含子查询或 PL/iSQL 表达式(例如,用户自定义函数的调用)。
- trigger_body
当触发器被激活时要执行的PL/iSQL 块或 CALL 子程序。CALL 子程序是 PL/iSQL 子程序。
如果 trigger_body 是 PL/iSQL 块并且包含错误,则 CREATE [OR REPLACE]语句会失败。
plsql_block详见[匿名块]{.underline}章节语法图。
routine_clause详见CALL调用章节语法图。
其中,declared_section 不能声明数据类型为 LONG 或 LONG RAW 的变量。
trigger_edition_clause
trigger_ordering_clause
instead_of_dml_trigger
介绍
使用instead_of_dml_trigger子句可以创建一个INSTEAD OF DML 触发器。
INSTEAD OF 触发器是一种专门用于视图(View) 的特殊类型 DML触发器。当尝试对视图执行 DML 操作(INSERT, UPDATE,DELETE)时,数据库不会执行该操作本身,而是转而执行在这个触发器中定义的代码。
使用INSTEAD OF 触发器需要注意以下几点:
如果视图本质上是可更新的并且具有 INSTEAD OF 触发器,则触发器优先:数据库触发触发器而不是在视图上执行 DML。
INSTEAD OF 触发器始终是行级触发器。INSTEAD OF触发器可以读取 OLD 值和 NEW 值,但不能更改它们。
当使用 INSTEAD OF 触发器对视图进行插入或更新时,不会强制执行视图的 WITH CHECK OPTION。INSTEAD OF 触发器主体必须强制执行检查。
语法说明
- DELETE
当 DELETE 语句从视图的表中删除一行时,会触发触发器。
- INSERT
如果触发器是在视图上创建的,则每当 INSERT 语句向定义视图的表中添加一行时,INSERT 都会触发触发器。
- UPDATE
如果触发器是在视图上创建的,则每当 UPDATE 语句更改定义视图的表的列中的值时,UPDATE 都会触发触发器。
- schema
模式名称。
- noneditioning_view
如果指定 nested_table_column,则 noneditioning_view是包含 nested_table_column 的视图的名称。否则,noneditioning_view 是要在其上创建触发器的视图的名称。
- FOR EACH ROW
仅用于文档,因为 INSTEAD OF 触发器始终是行触发器。
- ENABLE
(默认)创建处于启用状态的触发器。
- DISABLE
创建处于禁用状态的触发器,这可确保在启用触发器之前触发器编译时没有错误。
- trigger_body
当触发器被激活时要执行的PL/iSQL 块或 CALL 子程序。CALL子程序是 PL/iSQL子程序。
如果 trigger_body 是 PL/iSQL 块并且包含错误,则 CREATE [OR REPLACE]语句会失败。
declared_section 不能声明数据类型为 LONG 或 LONG RAW 的变量。
限制:
只支持视图
EDITIONABLE / NONEDITIONABLE (语法兼容)
FORWARD/REVERSE (语法兼容)
sharing_clause、default_collation_clause (语法兼容)
dml_event_clause
指定 simple_dml_trigger 的触发语句。数据库在现有用户事务中触发触发器。
- DELETE
每当 DELETE 语句从 table 或定义 view 的表中删除一行时,都会触发触发器。
- INSERT
每当 INSERT 语句向 table 或定义 view 的表添加一行时,都会触发触发器。
- UPDATE [ OF column [, column ] ]
每当 UPDATE 语句更改指定列中的值时,数据库都会触发触发器。默认值: 每当 UPDATE 语句更改 table 或定义 view 的表中的任何列中的值时,数据库都会触发触发器。
schema
要创建触发器的数据库对象的schema名称。
table
要创建触发器的数据库表或对象表的名称。
view
要创建触发器的数据库视图或对象视图的名称。
referencing_clause
指定关联名,分别指向当前行的旧值、新值和父值。 默认值: OLD 、 NEW 和PARENT 。
如果您的触发器与名为 OLD 、 NEW 或 PARENT表相关联,则使用此子句指定不同的关联名称,以避免表名和关联名之间的混淆。
trigger_edition_clause
将触发器创建为跨版本触发器,目前仅语法支持。
{ FORWARD | REVERSE } CROSSEDITION
限制:
不能在视图上定义跨版本触发器。
不能为跨版本触发器指定 NONEDITIONABLE。
选项:
- FORWARD
(默认)将触发器创建为正向交叉版本触发器。
- REVERSE
创建触发器作为反向跨版本触发器。
trigger_ordering_clause
{ FOLLOWS | PRECEDES } [ schmema.] trigger [ , [ schmema.] trigger
]...
- FOLLOWS | PRECEDES
指定具有相同时间点的触发器的相对触发方式。
使用 FOLLOWS 表示正在创建的触发器必须在指定触发器之后触发。
使用 PRECEDES 表示正在创建的触发器必须在指定触发器之前触发。
指定的触发器必须存在,并且必须已成功编译。它们不需要启用。
在下列定义中,A、B、C 和 D 是四个触发器:
如果 B 在其 FOLLOWS 子句中指定 A,则 B 直接跟随 A。
如果 C 直接跟随 B,且 B 直接跟随 A,则 C 间接跟随 A。
如果 D 直接跟随 C,且 C 间接跟随 A,则 D 间接跟随 A。
如果 B 直接或间接地遵循 A,则 B 明确遵循 A(即,B 和 A
的触发顺序由一个或多个 FOLLOWS 子句明确指定)。如果 A 直接或间接地位于 B 之前,则 A 明确位于 B 之前(即,A 和 B
的触发顺序由一个或多个 PRECEDES 子句明确指定)。
示例
示例1:简单的DML触发器
-- 创建表
CREATE TABLE empt (
emp_id NUMBER PRIMARY KEY,
name VARCHAR2(100),
salary NUMBER,
hire_date DATE
);
CREATE TABLE empt_logs (
log_id NUMBER,
emp_id NUMBER,
action VARCHAR2(100), -- 'INSERT', 'UPDATE', 'DELETE'
log_date DATE
);
CREATE TRIGGER trgins1
AFTER INSERT ON empt
FOR EACH ROW
BEGIN
INSERT INTO empt_logs (log_id, emp_id, action, log_date)
VALUES (5, :NEW.emp_id, 'trgins1->INSERT (1)',
to_date('03-09-2025','dd-mm-yyyy'));
END;
/
CREATE TRIGGER trgins2
AFTER INSERT ON empt
FOR EACH ROW
BEGIN
INSERT INTO empt_logs (log_id, emp_id, action, log_date)
VALUES (7, :NEW.emp_id, 'trgins2->INSERT (2)',
to_date('03-09-2025','dd-mm-yyyy'));
END;
/
CREATE TRIGGER at1
AFTER INSERT ON empt
FOR EACH ROW FOLLOWS trgins1, trgins2
BEGIN
INSERT INTO empt_logs (log_id, emp_id, action, log_date)
VALUES (8, :NEW.emp_id, 'at1->INSERT (3)',
to_date('03-09-2025','dd-mm-yyyy'));
END;
/
INSERT INTO empt VALUES (7387, 'TEST FOLLOWS', 900,
to_date('24-03-2025','dd-mm-yyyy'));
select * from empt;
select * from empt_logs;
两个表分别输出:
highgo=# select * from empt;
emp_id | name | salary | hire_date
--------+--------------+--------+------------
7387 | TEST FOLLOWS | 900 | 2025-03-24
highgo=# select * from empt_logs;
log_id | emp_id | action | log_date
--------+--------+---------------------+------------
7 | 7387 | trgins2->INSERT (2) | 2025-09-03
5 | 7387 | trgins1->INSERT (1) | 2025-09-03
8 | 7387 | at1->INSERT (3) | 2025-09-03
(3 行记录)
示例2:INSTEAD OF DML 触发器
--创建表
CREATE TABLE emp(
emp_no NUMBER,
emp_name VARCHAR2(50),
salary NUMBER,
manager VARCHAR2(50),
dept_no NUMBER
);
CREATE TABLE dept(
dept_no NUMBER,
dept_name VARCHAR2(50),
location VARCHAR2(50)
);
--插入数据
INSERT INTO dept VALUES(10,'HR','USA');
INSERT INTO dept VALUES(20,'SALES','UK');
INSERT INTO dept VALUES(30,'FINANCIAL','JAPAN');
INSERT INTO emp VALUES(1000,'XXX5',15000,'AAA',30);
INSERT INTO emp VALUES(1001,'YYY5',18000,'AAA',20) ;
INSERT INTO emp VALUES(1002,'ZZZ5',20000,'AAA',10);
COMMIT;
--创建视图
CREATE VIEW guru99_emp_view(
employee_name, dept_name,location) AS
SELECT emp.emp_name,dept.dept_name,dept.location
FROM emp, dept
WHERE emp.dept_no = dept.dept_no;
--创建触发器
CREATE TRIGGER guru99_view_modify_trg
INSTEAD OF UPDATE
ON guru99_emp_view
FOR EACH ROW
BEGIN
UPDATE dept
SET location=:new.location
WHERE dept_name=:old.dept_name;
END;
/
--测试
UPDATE guru99_emp_view SET location='FRANCE' WHERE
employee_name='XXX';
SELECT * FROM guru99_emp_view;
--输出:
employee_name | dept_name | location
---------------+-----------+----------
ZZZ5 | HR | USA
YYY5 | SALES | UK
XXX5 | FINANCIAL | JAPAN
(3 行记录)
ALTER TRIGGER
介绍
使用 ALTER TRIGGER 语句启用、禁用或编译数据库触发器。
此语句不会更改现有触发器的声明或定义。要重新声明或重新定义触发器,请使用带有 OR REPLACE 关键字的 CREATE TRIGGER 语句。
语法说明
- schema
触发器的schema名称。
- trigger_name
要修改的触发器名称。
- [ ENABLE | DISABLE ]
启用或者禁用触发器。
- RENAME TO new_name
重命名触发器而不更改其状态。
重命名触发器时,数据库会在USER_SOURCE 、 ALL_SOURCE 和 DBA_SOURCE 视图中重建记住的触发器源。因此,即使触发器源没有改变,这些视图的 TEXT 列中的注释和格式也可能会发生变化。
- { EDITIONABLE | NONEDITIONABLE }
仅语法支持。默认值:EDITIONABLE。
- trigger_compile_clause
- COMPILE
重新编译触发器,无论其是否有效。
- COMPILE
示例
--使用上文中创建的trgins1触发器
--禁用trigger
ALTER TRIGGER trgins1 DISABLE;
--启用trigger
ALTER TRIGGER trgins1 ENABLE;
--重命名trigger
ALTER TRIGGER trgins1 RENAME TO trgins1_rename;
--重新编译trigger
ALTER TRIGGER trgins1_rename COMPILE;
-- 检查触发器的状态
SELECT trigger_name, status FROM user_triggers WHERE trigger_name ='TRGINS1_RENAME';
DROP TRIGGER
介绍
使用 DROP TRIGGER 语句从数据库中删除数据库触发器。
语法说明
- schema
仅语法兼容。
说明:
当创建、修改、删除带scheam的触发器时,系统只检查schema是否存在。如果schema不存在则报错。
在HGDB中,触发器创建在表上,如果创建两个模式 a、b, 然后依次创建两个触发器 a.trg1 on emp , b.trg1 on emp,这种同名同表不同模式的触发器在创建时,b.trg1触发器会覆盖掉之前创建的a.trg1触发器,同名同表上触发器只能有一个。
- trigger
指定要删除的触发器的名称。数据库会将其从数据库中删除,并且不会再次触发它。
示例
--创建表t1和触发器trigger1
drop table t1;
create table t1(id int);
create or replace trigger trigger1
after insert on t1
for each row
BEGIN
dbms_output.put_line('t1');
END;
/
--设置屏幕显示dbms_output.put_line内容
set SERVEROUTPUT on;
--插入数据
insert into t1 values(1);
--输出内容:
t1
INSERT 0 1
--删除触发器
drop trigger trigger1;
--再次插入数据
insert into t1 values(1);
--输出内容:
INSERT 0 1