1. 简介
触发器是当特定事件出现时自动执行的存储过程,特定事件可以是执行更新的DML语句和DDL语句。触发器不能被显式调用,其有如下功能:
自动生成数据
自定义复杂的安全权限
提供审计和日志记录
启用复杂的业务逻辑
2. 创建
2.1 语法
触发器的创建语法如下,后面会以示例来说明:
1 | CREATE [OR REPLACE] TRIGGER trigger_name |
2.2 组成
触发器由三部分组成:
触发器语句(事件)
- 定义激活触发器的 DML 事件和 DDL 事件
触发器限制
- 执行触发器的条件,该条件必须为真才能激活触发器
触发器操作(主体)
- 包含一些 SQL 语句和代码,它们在发出了触发器语句且触发限制的值为真时运行
如下语法,可以简单地表现触发器的三个组成部分:
1 | CREATE [OR REPLACE] TRIGGER trigger_name |
2.3 示例说明
现有如下 student 表:
1 | SQL> select * from student; |
2.3.1 触发事件
现在我们对 student 表进行插入、更新或操作,其中触发事件设定为每次插入、删除或更新 student 的一条记录就会触发触发器执行。编写对应的触发器如下:
1 | create or replace trigger trig1 |
执行如下语句,其中的插入、更新和删除都会触发触发器的执行,并答应触发器 trig1 响应了》》》
:
1 | SQL> insert into student values (5, '李四', 52); |
2.3.2 for each row
创建如下触发器:
1 | create or replace trigger trig1 |
这里我们使用了for each row
短语,其作用是使表级触发器变为行级触发器。在没有增加该短语时,我们使用如下语句更新 student 的多条记录:
1 | update student set sage = sage + 10; |
触发器执行响应结果如下所示,由于是表级触发器,所以无论更新多少条记录,触发器只触发一次:
1 | SQL> update student set sage = sage + 10; |
再增加了 for each row
短语之后,触发器的响应结果如下所示,即每更新一条记录都会触发一次:
1 | SQL> update student set sage = sage + 10; |
2.3.3 before & after
before 的工作原理如下,简单地讲即是在数据入库之前触发触发器的执行:
after 的工作原理如下,简单地讲即是在数据入库之后触发触发器的执行:
现在我们创建如下触发器,即在对 student 表的 sage 列进行插入操作时,如果此时的学生记录的年龄若为负值则触发触发器执行,限制该记录入库,很明显我们应该需要使用 before 关键字,示例如下:
1 | create or replace trigger trig1 |
插入执行结果如下所示:
1 | SQL> insert into student values(5, 'lisi', -6); |
2.3.4 :new & :old
:new 表示一条新插入的记录,而 :old 表示原有的数据库数据记录。如果在触发器的 PL/SQL 块中使用 :new 或 :old,那么就必须是行级触发器,就是要有 for each row。很容易理解,因为 :new 和 :old 表示一行记录,所以触发器是需要作用于行的行级触发器,而不是作用于表的表级触发器。
需要说明的是,当执行 insert 的时候,:new 存在而 :old 不存在;当执行 delete 的时候,:new 不存在而:old 存在;当执行 update 的时候,:new 存在且 :old 也存在。由于 oracle 底层在执行 update 语句时,实质是先删除记录再插入新的记录的,所以才有:new 存在且:old 也存在的情况。
3. 类型
触发器有如下类型:
- DDL 触发器:在模式中执行 DDL 语句时执行,比如建表、修改表时触发的触发器;
- 数据库级触发器:在发生打开、关闭、登录和退出数据库等系统事件时执行;
- DML 触发器:在对表或视图执行DML语句时执行,比如更新、插入时触发的触发器;
- 表级触发器:无论受影响的行数是多少,都只执行一次;
- 行级触发器:对DML语句修改的每个行执行一次;
- INSTEAD OF 触发器:用于用户不能直接使用 DML 语句修改的视图;
4. INSTEAD OF 触发器
现在有 student 表和 score 如下所示:
1 | SQL> select * from score; |
创建如下多表视图:
1 | create view v_stu_sco |
视图的查询结果如下所示:
1 | SQL> select * from v_stu_sco; |
由于多表视图无法直接更新,我们可以使用 instead of 触发器来实现。示例如下:
1 | create or replace trigger upd_stu_sco_view |
执行如下更新语句,将学号为 3 的学生的成绩由 90 变为 100,具体如下:
1 | update v_stu_sco set score = 100 where sno = 3; |
执行结果如下所示:
1 | SQL> update v_stu_sco set score = 100 where sno = 3; |
5. 模式触发器
模式触发器也叫 DDL 触发器,即再对数据库对象进行操作的时所触发执行的触发器,示例如下:
1 | CREATE OR REPLACE TRIGGER log_drop_obj |
这里需要说明的是 ORA_DICT_OBJ_NAME 和 ORA_DICT_OBJ_TYPE 是 oracle 的系统变量,常用的系统变量如下所示:
Ora_client_ip_address 返回客户端的ip地址
Ora_database_name 返回当前数据库名
Ora_login_user 返回登录用户名
Ora_dict_obj_name 返回ddl操作所对应的数据库对象名
Ora_dict_obj_type 返回ddl操作所对应的数据库对象的类型
首先我们需要创建dropped_obj 表用于保存记录,创建语句如下所示:
1 | CREATE TABLE dropped_obj ( |
再创建上述的模式触发器之后,我们把 score 删除,dropped_obj 表中的记录如下所示:
1 | SQL> select * from dropped_obj; |
6. 数据库级触发器
数据库级触发器在发生打开、关闭、登录和退出数据库等系统事件时执行。下面通过示例说明,首先创建 log_table 表:
1 | create table log_table(username varchar2(20), logon_time date, logoff_time date, address varchar2(20)); |
接着创建如下触发器:
1 | create or replace trigger tr_logon |
在登录用户之后,会往 log_table 插入一条记录,如下所示:
1 | SQL> select * from log_table; |
7. 启用和禁用
示例如下所示:
1 | SQL> ALTER TRIGGER aiu_itemfile DISABLE; |
1 | SQL> ALTER TRIGGER aiu_itemfile ENABLE; |
8. 删除
示例如下所示:
1 | SQL> DROP TRIGGER aiu_itemfile; |
9. 触发器信息
USER_TRIGGERS 数据字典视图包含有关触发器的信息,查询示例如下所示:
1 | SQL> SELECT TRIGGER_NAME FROM USER_TRIGGERS |