马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
触发器是Oracle数据库中的一种特殊存储过程,它会在特定数据库事件发生时主动执行。触发器通常用于实现复杂的业务规则、数据验证、审计跟踪等功能。
目录
一、触发器基本概念
1. 触发器特点
2. 触发器构成要素
二、触发器范例
1. DML触发器
2. DDL触发器
3. 体系/数据库事件触发器
4. INSTEAD OF触发器
三、创建DML触发器
1. 基本语法
2. 行级触发器示例
3. 语句级触发器示例
四、特殊触发器
1. 复合触发器(11g+)
2. INSTEAD OF触发器(用于视图)
3. 体系事件触发器
五、触发器中的特殊变量和函数
1. OLD 和 NEW 伪记录
2. 事件属性函数
3. 条件谓词
4. 简单示例
5. 简单练习
六、触发器管理
1. 检察触发器
2. 启用/禁用触发器
3. 重新编译触发器
4. 删除触发器
七、触发器最佳实践
八、常见问题办理方案
1. 制止触发器递归
2. 处置惩罚大批量操纵
3. 跨数据库同步
九、触发器使用注意事项
一、触发器基本概念
触发器在数据库里以独立的对象存储,他与存储过程不同的是,存储过程通过其他步伐来启动运行或者直接运行,而触发器是由一个事件来启动运行。即触发器是当某个事件发生时主动地隐式运行,并且触发器不接收参数。
1. 触发器特点
- 主动执行:满意条件时由数据库主动触发
- 事件驱动:响应特定DML或DDL操纵
- 无显式调用:不能像存储过程那样直接调用
- 事务感知:作为触发语句的一部门执行
2. 触发器构成要素
- 触发事件:INSERT、UPDATE、DELETE等DML操纵
- 触发机遇:BEFORE或AFTER
- 触发级别:行级(FOR EACH ROW)或语句级
- 触发条件:WHEN子句定义的条件
- 触发体:PL/SQL代码块
二、触发器范例
1. DML触发器
响应数据操纵语言(DML)事件:
- INSERT
- UPDATE
- DELETE
- MERGE (10g+)
2. DDL触发器
响应数据定义语言(DDL)事件:
- CREATE
- ALTER
- DROP
- TRUNCATE等
3. 体系/数据库事件触发器
响应数据库体系事件:
- 登录/注销(LOGON/LOGOFF)
- 服务器错误(SERVERERROR)
- 启动/关闭(STARTUP/SHUTDOWN)
4. INSTEAD OF触发器
用于视图上的DML操纵
三、创建DML触发器
语句级触发器(statement):当某触发事件发生时,该触发器执行一次
行级触发器(row):当某触发事件发生时,受到影响的每一行数据,触发器都单独执行一次
1. 基本语法
- CREATE [OR REPLACE] TRIGGER trigger_name
- {BEFORE | AFTER | INSTEAD OF}
- {INSERT | UPDATE | DELETE [OR INSERT | UPDATE | DELETE...]}
- ON {table_name | view_name}
- [REFERENCING [OLD AS old] [NEW AS new]]
- [FOR EACH ROW]
- [WHEN (condition)]
- [DECLARE
- declaration_statements]
- BEGIN
- executable_statements
- [EXCEPTION
- exception_handling_statements]
- END [trigger_name];
- --化说明
- create or replace trigger tri_name--创建触发器
- {before/after}--触发的时间点{DML操作} on 表名
- {for each row}--加上是行级触发,不加是语句级
- {when 条件}
- begin
- {referencing {OLD {as} old|new {as} NEW}new PARENT as parent}---触发器的执行部分
- end;
- /
- -- 简单示例1
- create or replace trigger tr_1
- before delete on emp11
- begin
- insert into emp12
- (ename)
- select ename from emp where empno = 7369;
- end;
- /
- -- 简单示例2
- --创建一个触发器 当删除emp1表中的数据时,向emp2插入7788的员工信息
- --并且删除emp3中7788的信息。
- -- 准备
- create table emp1 as select * from emp;
- create table emp2 as select * from emp;
- create table emp3 as select * from emp;
- -- 创建触发器
- create or replace trigger tr_1
- before delete on emp1
- begin
- insert into emp2
- (ename, sal)
- select ename, sal from emp where empno = 7788;
- delete from emp3 where empno = 7788;
- end;
- /
- -- 触发
- delete from emp1 where empno=7788;
复制代码 2. 行级触发器示例
- -- 薪资变更审计触发器
- CREATE OR REPLACE TRIGGER audit_salary_change
- BEFORE UPDATE OF salary ON employees
- FOR EACH ROW
- WHEN (NEW.salary <> OLD.salary)
- DECLARE
- v_change_type VARCHAR2(10);
- BEGIN
- -- 确定变更类型
- IF :NEW.salary > :OLD.salary THEN
- v_change_type := 'RAISE';
- ELSE
- v_change_type := 'CUT';
- END IF;
-
- -- 记录审计信息
- INSERT INTO salary_audit (
- audit_id, employee_id,
- old_salary, new_salary,
- change_type, change_date, changed_by
- ) VALUES (
- audit_seq.NEXTVAL, :NEW.employee_id,
- :OLD.salary, :NEW.salary,
- v_change_type, SYSDATE, USER
- );
- END audit_salary_change;
- /
复制代码 3. 语句级触发器示例
- -- 限制非工作时间操作
- CREATE OR REPLACE TRIGGER restrict_after_hours_dml
- BEFORE INSERT OR UPDATE OR DELETE ON employees
- DECLARE
- v_current_time VARCHAR2(8) := TO_CHAR(SYSDATE, 'HH24:MI:SS');
- v_day_of_week VARCHAR2(3) := TO_CHAR(SYSDATE, 'DY');
- BEGIN
- -- 工作日晚上8点到早上6点禁止操作
- IF v_day_of_week NOT IN ('SAT', 'SUN')
- AND (v_current_time > '20:00:00' OR v_current_time < '06:00:00') THEN
- RAISE_APPLICATION_ERROR(-20001,
- '非工作时间(工作日6:00-20:00)不允许修改员工数据');
- END IF;
- END restrict_after_hours_dml;
- /
复制代码 四、特殊触发器
1. 复合触发器(11g+)
- CREATE OR REPLACE TRIGGER compound_emp_trigger
- FOR INSERT OR UPDATE OR DELETE ON employees
- COMPOUND TRIGGER
- -- 在触发语句开始前执行
- BEFORE STATEMENT IS
- BEGIN
- DBMS_OUTPUT.PUT_LINE('开始员工数据变更操作');
- END BEFORE STATEMENT;
-
- -- 每行变更前执行
- BEFORE EACH ROW IS
- BEGIN
- DBMS_OUTPUT.PUT_LINE('准备变更员工ID: ' ||
- NVL(:NEW.employee_id, :OLD.employee_id));
- END BEFORE EACH ROW;
-
- -- 每行变更后执行
- AFTER EACH ROW IS
- BEGIN
- DBMS_OUTPUT.PUT_LINE('已完成变更员工ID: ' ||
- NVL(:NEW.employee_id, :OLD.employee_id));
- END AFTER EACH ROW;
-
- -- 在触发语句结束后执行
- AFTER STATEMENT IS
- BEGIN
- DBMS_OUTPUT.PUT_LINE('员工数据变更操作完成');
- END AFTER STATEMENT;
- END compound_emp_trigger;
- /
复制代码 2. INSTEAD OF触发器(用于视图)
- -- 创建复杂视图
- CREATE OR REPLACE VIEW emp_dept_view AS
- SELECT e.employee_id, e.last_name, e.salary,
- d.department_id, d.department_name
- FROM employees e JOIN departments d
- ON e.department_id = d.department_id;
- -- 创建INSTEAD OF触发器支持插入
- CREATE OR REPLACE TRIGGER io_emp_dept_insert
- INSTEAD OF INSERT ON emp_dept_view
- FOR EACH ROW
- DECLARE
- v_dept_id NUMBER;
- BEGIN
- -- 检查部门是否存在
- BEGIN
- SELECT department_id INTO v_dept_id
- FROM departments
- WHERE department_name = :NEW.department_name;
- EXCEPTION
- WHEN NO_DATA_FOUND THEN
- RAISE_APPLICATION_ERROR(-20001,
- '部门 ' || :NEW.department_name || ' 不存在');
- END;
-
- -- 插入员工记录
- INSERT INTO employees (
- employee_id, last_name, salary, department_id
- ) VALUES (
- employees_seq.NEXTVAL, :NEW.last_name,
- :NEW.salary, v_dept_id
- );
- END io_emp_dept_insert;
- /
复制代码 3. 体系事件触发器
- -- 登录审计触发器
- CREATE OR REPLACE TRIGGER logon_audit_trigger
- AFTER LOGON ON DATABASE
- BEGIN
- INSERT INTO logon_audit (
- audit_id, username,
- logon_time, host, ip_address
- ) VALUES (
- logon_seq.NEXTVAL, USER,
- SYSDATE, SYS_CONTEXT('USERENV', 'HOST'),
- SYS_CONTEXT('USERENV', 'IP_ADDRESS')
- );
- END logon_audit_trigger;
- /
- -- DDL变更审计触发器
- CREATE OR REPLACE TRIGGER audit_ddl_changes
- AFTER DDL ON SCHEMA
- BEGIN
- INSERT INTO ddl_audit (
- audit_id, username,
- event_type, object_type,
- object_name, change_date
- ) VALUES (
- ddl_audit_seq.NEXTVAL, USER,
- ORA_SYSEVENT, ORA_DICT_OBJ_TYPE,
- ORA_DICT_OBJ_NAME, SYSDATE
- );
- END audit_ddl_changes;
- /
复制代码 五、触发器中的特殊变量和函数
1. OLD 和 NEW 伪记录
- :OLD:引用操纵前的行值(UPDATE/DELETE)---old.列名:此列中的旧值
- :NEW:引用操纵后的行值(INSERT/UPDATE)---new.列名:此列中的新值
2. 事件属性函数
- ORA_SYSEVENT:触发事件的名称
- ORA_DICT_OBJ_TYPE:DDL操纵的对象范例
- ORA_DICT_OBJ_NAME:DDL操纵的对象名称
- ORA_IS_ALTER_COLUMN:检查是否修改了特定列
3. 条件谓词
- INSERTING:触发器由INSERT触发时为TRUE
- UPDATING:触发器由UPDATE触发时为TRUE
- DELETING:触发器由DELETE触发时为TRUE
条件谓语 | old
| new
| update
| 有
| 有
| delete
| 有
| null
| insert
| null
| 有
| 4. 简单示例
- --创建一个触发器
- --当emp1被更新是,将更新前和更新后的ename和empno插入到emp2
- --的对应列,在job列显示更新前还是更新后,并且hiredate列插入更新时间
- CREATE OR REPLACE TRIGGER TRE_1
- BEFORE UPDATE ON EMP1
- FOR EACH ROW
- BEGIN
- INSERT INTO EMP2 (ENAME, EMPNO,job,hiredate) VALUES (:old.ename,:old.empno,'更新前',sysdate);
- INSERT INTO EMP2 (ENAME, EMPNO,job,hiredate) VALUES (:new.ename,:new.empno,'更新后',SYSDATE);
- END;
- TRUNCATE TABLE emp2
- UPDATE emp1 SET sal=sal+10000;
- SELECT * FROM emp2
复制代码特性 | INSERT | UPDATE | DELETE | OLD | NULL | 现实值 | 现实值 | NEW | 现实值 | 现实值 | NULL | 5. 简单练习
- --创建一张emp1和emp内容一样。创建emp2只保留emp的格式。
- --创建一个触发器
- --当emp1表被更新时触发,将更新前和更新后的ename,empno插入到emp2中
- -- 创建触发器
- create or replace trigger tr_3
- before update on emp1
- for each row
- begin
- insert into emp2 (ename,empno,job) values(:old.ename,:old.empno,'old');
- insert into emp2 (ename,empno,job) values(:new.ename,:new.empno,'new');
- end;
- /
- -- 触发
- update emp1 set empno=empno-7000,ename=lower(ename);
- -- 查询验证变化
- select * from emp1;
- select * from emp2;
复制代码 六、触发器管理
1. 检察触发器
- -- 查看触发器定义
- SELECT trigger_name, trigger_type, triggering_event, table_name, status
- FROM user_triggers;
- -- 查看触发器源代码
- SELECT text FROM user_source
- WHERE name = 'AUDIT_SALARY_CHANGE' AND type = 'TRIGGER'
- ORDER BY line;
复制代码 2. 启用/禁用触发器
- -- 禁用单个触发器
- ALTER TRIGGER audit_salary_change DISABLE;
- -- 启用单个触发器
- ALTER TRIGGER audit_salary_change ENABLE;
- -- 禁用表上的所有触发器
- ALTER TABLE employees DISABLE ALL TRIGGERS;
- -- 启用表上的所有触发器
- ALTER TABLE employees ENABLE ALL TRIGGERS;
复制代码 3. 重新编译触发器
- ALTER TRIGGER trigger_name COMPILE;
复制代码 4. 删除触发器
- DROP TRIGGER trigger_name;
复制代码 七、触发器最佳实践
- 保持简便:触发器应简短高效,制止复杂业务逻辑
- 制止递归:注意触发器大概导致的级联触发
- 思量性能:行级触发器对大批量操纵影响较大
- 明确文档:记录触发器目的和业务规则
- 非常处置惩罚:妥善处置惩罚大概出现的错误
- 制止事务控制:通常不应在触发器中提交或回滚
- 测试充分:验证触发器在各种场景下的举动
八、常见问题办理方案
1. 制止触发器递归
- CREATE OR REPLACE TRIGGER prevent_recursion
- BEFORE UPDATE ON employees
- FOR EACH ROW
- DECLARE
- v_recursion_flag BOOLEAN := FALSE;
- BEGIN
- -- 检查是否由触发器调用
- IF UPDATING AND DBMS_UTILITY.FORMAT_CALL_STACK LIKE '%PREVENT_RECURSION%' THEN
- v_recursion_flag := TRUE;
- END IF;
-
- -- 如果不是递归调用,则执行业务逻辑
- IF NOT v_recursion_flag THEN
- -- 业务逻辑代码
- END IF;
- END;
- /
复制代码 2. 处置惩罚大批量操纵
- -- 使用BULK COLLECT和FORALL优化
- CREATE OR REPLACE TRIGGER optimize_bulk_operation
- AFTER INSERT ON large_table
- DECLARE
- TYPE id_array IS TABLE OF large_table.id%TYPE;
- v_ids id_array;
- BEGIN
- -- 批量收集新插入的ID
- SELECT id BULK COLLECT INTO v_ids
- FROM large_table
- WHERE status = 'NEW';
-
- -- 批量处理
- FORALL i IN 1..v_ids.COUNT
- UPDATE related_table
- SET last_updated = SYSDATE
- WHERE large_table_id = v_ids(i);
- END;
- /
复制代码 3. 跨数据库同步
- CREATE OR REPLACE TRIGGER sync_cross_database
- AFTER INSERT OR UPDATE OR DELETE ON local_table
- FOR EACH ROW
- DECLARE
- PRAGMA AUTONOMOUS_TRANSACTION;
- BEGIN
- IF INSERTING THEN
- INSERT INTO remote_table@remote_db VALUES (:NEW.id, :NEW.name);
- ELSIF UPDATING THEN
- UPDATE remote_table@remote_db
- SET name = :NEW.name
- WHERE id = :OLD.id;
- ELSIF DELETING THEN
- DELETE FROM remote_table@remote_db WHERE id = :OLD.id;
- END IF;
- COMMIT;
- EXCEPTION
- WHEN OTHERS THEN
- ROLLBACK;
- -- 记录错误但不中断主事务
- INSERT INTO error_log VALUES (SYSDATE, 'sync_cross_database', SQLERRM);
- COMMIT;
- END;
- /
复制代码 九、触发器使用注意事项
编写触发器时,必要注意一下几点:
(1)触发器不担当参数
(2)一个表上最多可有12个触发器,但同一时间、同一事件、同一范例的触发器只能有一个。并且各触发器之间不能有矛盾。
(3)在一个表上的触发器越多,对在该表上的DML操纵的性能影响就越大
(4)触发器最大为32KB。若确实必要,可以先建立过程,然后在触发器中用CALL语句进行调用
(5)在触发器的执行部门只能用DML语句(SELECT、INSERT、UDATE、DELETE),不能使用DDL语句(CREATE、ALTER、DROP)
(6)触发器中不能包罗事物控制语句(COMMIT、ROLLBACK、SAVEPOINT)。因为触发器是触发语句的一部门,触发语句被提交、回退时,触发器也被提交、回退了。
(7)在触发器主体中调用的任何过程、函数,都不能使用事务控制语句
(8)在触发器主体中不能声明任何Long和Blob变量。新值new和旧值old也不能向表中的任何Long和Blob列。
(9)不同范例的触发器(如DML触发器、INSTEAD OF触发器、体系触发器)的语法格式和作用有比力大区别
触发器是Oracle数据库强大的功能,公道使用可以实现复杂的业务规则、数据完备性和审计需求。但也需谨慎使用,制止过度依赖触发器导致体系难以维护。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|