Oracle 数据库触发器高级应用
Oracle 数据库触发器高级应用
适用版本:Oracle Database 10g / 11g / 12c / 19c / 23ai 文档版本:v1.0 / 2026-07
1. 概述
触发器高级应用场景[1]:
详细见:Oracle PL/SQL 触发器详解。
2. 审计触发器
2.1 通用审计
CREATE TABLE audit_log (
id NUMBER GENERATED ALWAYS AS IDENTITY,
table_name VARCHAR2(50),
operation VARCHAR2(10),
row_id ROWID,
old_values CLOB,
new_values CLOB,
user_name VARCHAR2(50),
action_time TIMESTAMP
);
CREATE OR REPLACE TRIGGER trg_audit_employees
AFTER INSERT OR UPDATE OR DELETE ON employees
FOR EACH ROW
DECLARE
v_op VARCHAR2(10);
v_old CLOB;
v_new CLOB;
BEGIN
v_op := CASE WHEN INSERTING THEN 'INSERT'
WHEN UPDATING THEN 'UPDATE'
WHEN DELETING THEN 'DELETE' END;
v_old := :OLD.id || ',' || :OLD.name || ',' || :OLD.salary;
v_new := :NEW.id || ',' || :NEW.name || ',' || :NEW.salary;
INSERT INTO audit_log (table_name, operation, row_id, old_values, new_values, user_name, action_time)
VALUES ('EMPLOYEES', v_op, :OLD.ROWID, v_old, v_new, USER, SYSTIMESTAMP);
END;
/
2.2 FGA 替代
-- Fine-Grained Auditing
BEGIN
DBMS_FGA.ADD_POLICY(
object_schema => 'SCOTT',
object_name => 'employees',
policy_name => 'audit_sensitive',
audit_condition => 'salary > 10000',
audit_column => 'salary',
handler_schema => 'SCOTT',
handler_module => 'audit_handler'
);
END;
/
详细见:Oracle 审计详解。
3. 派生列
3.1 计算列
CREATE TABLE order_items (
id NUMBER PRIMARY KEY,
quantity NUMBER,
price NUMBER,
total NUMBER
);
CREATE OR REPLACE TRIGGER trg_calc_total
BEFORE INSERT OR UPDATE OF quantity, price ON order_items
FOR EACH ROW
BEGIN
:NEW.total := :NEW.quantity * :NEW.price;
END;
/
-- 12c+ 虚拟列
CREATE TABLE order_items (
id NUMBER PRIMARY KEY,
quantity NUMBER,
price NUMBER,
total NUMBER GENERATED ALWAYS AS (quantity * price) VIRTUAL
);
3.2 默认值
CREATE OR REPLACE TRIGGER trg_default
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
IF :NEW.created_at IS NULL THEN
:NEW.created_at := SYSTIMESTAMP;
END IF;
IF :NEW.status IS NULL THEN
:NEW.status := 'ACTIVE';
END IF;
IF :NEW.id IS NULL THEN
SELECT seq_emp.NEXTVAL INTO :NEW.id FROM dual;
END IF;
END;
/
-- 12c+ IDENTITY / DEFAULT
CREATE TABLE employees (
id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
status VARCHAR2(20) DEFAULT 'ACTIVE',
created_at TIMESTAMP DEFAULT SYSTIMESTAMP
);
详细见:Oracle 序列与自增列详解。
4. 数据完整性
4.1 跨表约束
CREATE OR REPLACE TRIGGER trg_check_dept_budget
BEFORE INSERT OR UPDATE OF amount ON expenses
FOR EACH ROW
DECLARE
v_budget NUMBER;
v_total NUMBER;
BEGIN
SELECT budget INTO v_budget FROM departments WHERE id = :NEW.dept_id;
SELECT NVL(SUM(amount), 0) INTO v_total
FROM expenses WHERE dept_id = :NEW.dept_id;
IF v_total + :NEW.amount > v_budget THEN
RAISE_APPLICATION_ERROR(-20001, 'Budget exceeded');
END IF;
END;
/
4.2 复杂业务规则
CREATE OR REPLACE TRIGGER trg_check_manager
BEFORE INSERT OR UPDATE OF manager_id ON employees
FOR EACH ROW
DECLARE
v_count NUMBER;
BEGIN
-- 经理必须是同部门
SELECT COUNT(*) INTO v_count
FROM employees
WHERE id = :NEW.manager_id AND dept_id = :NEW.dept_id;
IF v_count = 0 THEN
RAISE_APPLICATION_ERROR(-20002, 'Manager must be in same dept');
END IF;
END;
/
5. 变异表
5.1 问题
-- 错误:ORA-04091
CREATE OR REPLACE TRIGGER trg_bad
AFTER INSERT ON employees
FOR EACH ROW
DECLARE
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count FROM employees WHERE dept_id = :NEW.dept_id;
-- ERROR: employees is mutating
END;
/
5.2 复合触发器
CREATE OR REPLACE TRIGGER trg_check_dept_size
FOR INSERT OR UPDATE ON employees
COMPOUND TRIGGER
TYPE id_list IS TABLE OF NUMBER;
v_dept_ids id_list := id_list();
AFTER EACH ROW IS
BEGIN
v_dept_ids.EXTEND;
v_dept_ids(v_dept_ids.LAST) := :NEW.dept_id;
END AFTER EACH ROW;
AFTER STATEMENT IS
v_count NUMBER;
BEGIN
FOR i IN 1..v_dept_ids.COUNT LOOP
SELECT COUNT(*) INTO v_count FROM employees WHERE dept_id = v_dept_ids(i);
IF v_count > 100 THEN
-- 处理
END IF;
END LOOP;
END AFTER STATEMENT;
END;
/
详细见:Oracle PL/SQL 触发器详解。
6. 同步
6.1 表同步
CREATE OR REPLACE TRIGGER trg_sync_emp
AFTER INSERT OR UPDATE OR DELETE ON employees
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO employees_backup VALUES (:NEW.id, :NEW.name, :NEW.salary, SYSTIMESTAMP);
ELSIF UPDATING THEN
UPDATE employees_backup
SET name = :NEW.name, salary = :NEW.salary, updated_at = SYSTIMESTAMP
WHERE id = :NEW.id;
ELSIF DELETING THEN
DELETE FROM employees_backup WHERE id = :OLD.id;
END IF;
END;
/
6.2 物化视图日志
- 推荐
- 异步
- 性能
详细见:Oracle 视图与物化视图详解。
7. INSTEAD OF
7.1 复杂视图
CREATE OR REPLACE VIEW emp_dept_view AS
SELECT e.id, e.name, e.salary, d.id AS dept_id, d.dept_name
FROM employees e, departments d
WHERE e.dept_id = d.id;
CREATE OR REPLACE TRIGGER trg_emp_dept_view
INSTEAD OF INSERT OR UPDATE OR DELETE ON emp_dept_view
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO employees (id, name, salary, dept_id)
VALUES (:NEW.id, :NEW.name, :NEW.salary, :NEW.dept_id);
ELSIF UPDATING THEN
UPDATE employees SET name = :NEW.name, salary = :NEW.salary
WHERE id = :NEW.id;
UPDATE departments SET dept_name = :NEW.dept_name
WHERE id = :NEW.dept_id;
ELSIF DELETING THEN
DELETE FROM employees WHERE id = :OLD.id;
END IF;
END;
/
详细见:Oracle 视图与物化视图详解。
8. 事件触发器
8.1 DDL
CREATE OR REPLACE TRIGGER trg_ddl_protect
BEFORE DROP OR TRUNCATE ON SCHEMA
BEGIN
IF ORA_DICT_OBJ_NAME LIKE 'SYS_%' THEN
RAISE_APPLICATION_ERROR(-20003, 'Cannot drop system objects');
END IF;
END;
/
8.2 数据库
CREATE OR REPLACE TRIGGER trg_logon
AFTER LOGON ON DATABASE
BEGIN
INSERT INTO login_log (username, logon_time, ip)
VALUES (SYS_CONTEXT('USERENV', 'SESSION_USER'),
SYSTIMESTAMP,
SYS_CONTEXT('USERENV', 'IP_ADDRESS'));
END;
/
9. 自治事务
CREATE OR REPLACE TRIGGER trg_log
AFTER INSERT ON employees
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO log VALUES (:NEW.id, 'Hired', SYSTIMESTAMP);
COMMIT; -- 必须提交
END;
/
10. 性能
10.1 开销
- 每行触发
- DML 性能
- 谨慎
10.2 替代
- 约束(CHECK)
- 默认值
- 虚拟列(12c+)
- 应用层
- 物化视图日志
详细见:Oracle PL/SQL 性能优化详解。
11. 应用场景
11.1 审计
-- 完整审计
AFTER INSERT OR UPDATE OR DELETE ...
-- 记录所有变更
11.2 业务规则
-- 复杂约束
BEFORE INSERT OR UPDATE ...
-- 跨表检查
11.3 同步
-- 表同步
AFTER ...
-- 备份 / 物化
11.4 事件
-- DDL / 数据库事件
-- 日志 / 安全
12. 禁用
-- 批量加载
ALTER TABLE employees DISABLE ALL TRIGGERS;
-- 加载
INSERT /*+ APPEND */ INTO employees SELECT * FROM source;
-- 启用
ALTER TABLE employees ENABLE ALL TRIGGERS;
13. 常见坑与排错
13.1 变异表
- ORA-04091
- 复合触发器
- 临时表
13.2 递归
- 触发器引发触发器
- 避免
13.3 性能
- 行级开销
- 批量禁用
13.4 事务
- 自治事务
- 日志
14. 最佳实践
- 谨慎使用:性能
- 业务最小:简单
- 复合触发器:变异表
- 自治事务:日志
- 约束优先:替代
- 审计:合规
- 禁用批量:加载
- INSTEAD OF:视图
- 事件触发器:管理
- 测试:完整
15. 参考资料
[1] Oracle Database PL/SQL Language Reference 19c, “Triggers” https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/plsql-triggers.html