一、触发器基础概念
1.1 触发器概述
触发器是DM8数据库中一种与表关联的特殊存储过程,它不需要用户手动调用,而是由数据库在特定事件发生时自动执行。在管理触发器的过程中,理解其核心机制至关重要。
触发器主要由以下要素构成:
- 触发事件:DML操作(INSERT、UPDATE、DELETE)、DDL操作或数据库系统事件
- 触发时机:BEFORE(事件前触发)、AFTER(事件后触发)、INSTEAD OF(替代触发)
- 触发级别:语句级触发器、行级触发器
- 触发对象:表、视图、数据库、模式
1.2 触发器分类
DM8支持三大类触发器,具体分类如下:
| 触发器类型 | 触发事件 | 典型应用 |
| --- | --- | --- |
| DML触发器 | INSERT/UPDATE/DELETE | 数据校验、审计日志 |
| DDL触发器 | CREATE/ALTER/DROP | 结构变更追踪 |
| 系统触发器 | LOGIN/LOGOFF/STARTUP | 会话审计、启动任务 |
1.3 触发器执行流程
触发器执行遵循严格的调用链,其完整流程如下所示:
二、创建各类触发器
2.1 创建DML触发器
DML触发器是日常管理触发器工作中最常见的类型,其基本语法如下:
CREATE OR REPLACE TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF}
{INSERT | DELETE | UPDATE [OF column_list]}
ON table_name
[FOR EACH ROW]
[WHEN condition]
BEGIN
-- 触发器逻辑
END;
/
下面给出一个具体的DML触发器示例,用于在员工薪资调整时自动记录审计日志:
-- 创建审计日志表
CREATE TABLE emp_audit_log (
log_id BIGINT IDENTITY(1,1) PRIMARY KEY,
emp_id INT,
old_salary DECIMAL(10,2),
new_salary DECIMAL(10,2),
operator VARCHAR(50),
operate_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建行级DML触发器
CREATE OR REPLACE TRIGGER trg_emp_salary_audit
AFTER UPDATE OF salary
ON employee
FOR EACH ROW
BEGIN
INSERT INTO emp_audit_log(emp_id, old_salary, new_salary, operator)
VALUES(:OLD.employee_id, :OLD.salary, :NEW.salary, USER);
END;
/
2.2 创建DDL触发器
DDL触发器用于追踪数据库结构变更,是管理触发器时用于审计的重要工具:
CREATE OR REPLACE TRIGGER trg_ddl_monitor
AFTER CREATE OR ALTER OR DROP
ON DATABASE
BEGIN
INSERT INTO ddl_log(event_type, object_owner, object_name, object_type, operator, operate_time)
VALUES(SYSEVENT.EVENT_TYPE, SYSEVENT.OBJECT_OWNER, SYSEVENT.OBJECT_NAME,
SYSEVENT.OBJECT_TYPE, USER, CURRENT_TIMESTAMP);
END;
/
DDL触发器可用的系统事件属性包括:
- SYSEVENT.EVENT_TYPE:事件类型
- SYSEVENT.OBJECT_OWNER:对象所属模式
- SYSEVENT.OBJECT_NAME:对象名称
- SYSEVENT.OBJECT_TYPE:对象类型
2.3 创建系统触发器
系统触发器响应数据库级事件,适合用于登录审计与会话管理:
-- 创建登录日志表
CREATE TABLE login_log(
log_id BIGINT IDENTITY(1,1) PRIMARY KEY,
login_user VARCHAR(50),
login_time TIMESTAMP,
client_ip VARCHAR(50),
status VARCHAR(20)
);
-- 创建登录触发器
CREATE OR REPLACE TRIGGER trg_login_audit
AFTER LOGON
ON DATABASE
BEGIN
INSERT INTO login_log(login_user, login_time, client_ip, status)
VALUES(USER, CURRENT_TIMESTAMP, SYS_CONTEXT('USERENV','IP_ADDRESS'), 'SUCCESS');
END;
/
2.4 创建INSTEAD OF触发器
INSTEAD OF触发器主要用于视图,因为很多视图不支持直接DML操作:
CREATE OR REPLACE VIEW emp_dept_view AS
SELECT e.employee_id, e.employee_name, e.salary, d.department_name
FROM employee e
INNER JOIN department d ON e.department_id = d.department_id;
CREATE OR REPLACE TRIGGER trg_emp_dept_view_insert
INSTEAD OF INSERT
ON emp_dept_view
FOR EACH ROW
BEGIN
INSERT INTO employee(employee_id, employee_name, salary)
VALUES(:NEW.employee_id, :NEW.employee_name, :NEW.salary);
END;
/
三、状态控制与维护
3.1 启用与禁用触发器
在生产环境中,批量数据导入时常需要临时禁用触发器以提升性能:
-- 禁用单个触发器
ALTER TRIGGER trg_emp_salary_audit DISABLE;
-- 启用单个触发器
ALTER TRIGGER trg_emp_salary_audit ENABLE;
-- 禁用某表上所有触发器
ALTER TABLE employee DISABLE ALL TRIGGERS;
-- 启用某表上所有触发器
ALTER TABLE employee ENABLE ALL TRIGGERS;
触发器状态切换流程如下:
3.2 修改触发器
DM8支持通过CREATE OR REPLACE方式重编译触发器,也可以单独重新编译:
-- 通过替换方式修改触发器逻辑
CREATE OR REPLACE TRIGGER trg_emp_salary_audit
AFTER UPDATE OF salary
ON employee
FOR EACH ROW
WHEN (NEW.salary <> OLD.salary)
BEGIN
INSERT INTO emp_audit_log(emp_id, old_salary, new_salary, operator)
VALUES(:OLD.employee_id, :OLD.salary, :NEW.salary, USER);
END;
/
-- 重新编译触发器
ALTER TRIGGER trg_emp_salary_audit COMPILE;
3.3 删除触发器
当触发器不再使用时,应及时清理以避免维护负担:
-- 删除指定触发器
DROP TRIGGER trg_emp_salary_audit;
-- 删除时若触发器不存在可使用IF EXISTS避免报错
DROP TRIGGER IF EXISTS trg_emp_salary_audit;
3.4 重命名触发器
ALTER TRIGGER trg_old_name RENAME TO trg_new_name;
四、信息查询与调试
4.1 查询触发器元数据
DM8提供多张数据字典视图用于管理触发器:
-- 查询当前用户下所有触发器
SELECT trigger_name, table_name, status, triggering_event
FROM USER_TRIGGERS;
-- 查询所有触发器(DBA权限)
SELECT owner, trigger_name, table_owner, table_name, status
FROM DBA_TRIGGERS
WHERE owner = 'SYSDBA';
-- 查看触发器源码
SELECT text FROM ALL_SOURCE
WHERE name = 'TRG_EMP_SALARY_AUDIT'
ORDER BY line;
常用数据字典视图一览:
| 视图名称 | 说明 |
| --- | --- |
| USER_TRIGGERS | 当前用户拥有的触发器 |
| ALL_TRIGGERS | 当前用户可访问的触发器 |
| DBA_TRIGGERS | 数据库中所有触发器 |
| ALL_SOURCE | 触发器源代码 |
4.2 触发器调试方法
触发器调试是管理触发器过程中较有挑战的环节,可采用以下方法:
-- 方法一:使用PRINT输出调试信息
CREATE OR REPLACE TRIGGER trg_debug_demo
AFTER INSERT
ON test_table
FOR EACH ROW
BEGIN
PRINT '插入数据:ID=' || :NEW.id;
END;
/
-- 方法二:将调试信息写入日志表
CREATE TABLE trigger_debug(msg VARCHAR(500), log_time TIMESTAMP);
CREATE OR REPLACE TRIGGER trg_debug_demo
AFTER INSERT
ON test_table
FOR EACH ROW
BEGIN
INSERT INTO trigger_debug VALUES('触发器执行,新ID:' || :NEW.id, CURRENT_TIMESTAMP);
END;
/
触发器调试排查流程:
4.3 查看触发器编译错误
SELECT name, type, line, position, text
FROM USER_ERRORS
WHERE name = 'TRG_EMP_SALARY_AUDIT'
ORDER BY sequence;
五、实战案例与最佳实践
5.1 业务规则约束案例
下面演示一个复杂的业务规则触发器,用于限制部门薪资总额上限:
CREATE OR REPLACE TRIGGER trg_dept_salary_limit
BEFORE INSERT OR UPDATE OF salary
ON employee
FOR EACH ROW
DECLARE
v_total_salary DECIMAL(12,2);
v_limit DECIMAL(12,2) := 1000000;
BEGIN
SELECT NVL(SUM(salary),0) + :NEW.salary
INTO v_total_salary
FROM employee
WHERE department_id = :NEW.department_id;
IF v_total_salary > v_limit THEN
RAISE_APPLICATION_ERROR(-20001,
'部门薪资总额超过上限,当前总额:' || v_total_salary);
END IF;
END;
/
5.2 数据同步案例
通过触发器实现多表数据同步,是管理触发器常见需求:
CREATE OR REPLACE TRIGGER trg_sync_customer
AFTER INSERT OR UPDATE OR DELETE
ON customer
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO customer_bak(id, name, phone, sync_time)
VALUES(:NEW.id, :NEW.name, :NEW.phone, CURRENT_TIMESTAMP);
ELSIF UPDATING THEN
UPDATE customer_bak
SET name = :NEW.name, phone = :NEW.phone, sync_time = CURRENT_TIMESTAMP
WHERE id = :NEW.id;
ELSIF DELETING THEN
DELETE FROM customer_bak WHERE id = :OLD.id;
END IF;
END;
/
5.3 管理触发器最佳实践
在长期管理触发器的过程中,建议遵循以下原则:
- 触发器逻辑应尽量简单,复杂业务交给存储过程处理
- 避免在触发器中引发递归调用
- 行级触发器中慎用SELECT查询同表数据,可能造成变异表错误
- 大批量数据导入前先DISABLE触发器
- 为触发器命名应统一规范,如trg_表名_操作_用途
- 触发器中应捕获异常并记录日志,避免影响主事务
5.4 常见问题与解决方案
| 问题现象 | 可能原因 | 解决方案 |
| --- | --- | --- |
| 变异表错误 | 行级触发器查询本表 | 使用自治事务或复合触发器 |
| 触发器不执行 | 触发器被DISABLE | ALTER TRIGGER ENABLE |
| 性能急剧下降 | 触发器中含复杂SQL | 简化逻辑或迁移至存储过程 |
| 编译报错 | :NEW和:OLD使用错误 | 检查BEFORE/AFTER及DML类型 |
完整的管理触发器工作流总览:
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/qq_41840843/article/details/163573886




