场景:企业规定仅能在工作日(周一至周五)8:00-18:00录入新员工信息,需通过触发器强制约束。
案例代码:
CREATE OR REPLACE TRIGGER trg_restrict_emp_insert
BEFORE INSERT ON employees
DECLARE
v_day VARCHAR2(10);
v_hour NUMBER;
BEGIN
-- 获取当前星期几(1-7,1=周日)
v_day := TO_CHAR(SYSDATE, 'DY');
-- 获取当前小时(24小时制)
v_hour := TO_NUMBER(TO_CHAR(SYSDATE, 'HH24'));
-- 检查是否为周末或非工作时间
IF v_day IN ('SAT', 'SUN') OR v_hour < 8 OR v_hour >= 18 THEN
RAISE_APPLICATION_ERROR(-20001, '非工作时间(周末/8:00-18:00外)禁止插入员工数据!');
END IF;
END;
/
说明:此触发器为BEFORE STATEMENT类型(默认),在employees表插入操作前执行,通过RAISE_APPLICATION_ERROR阻止非法操作。
场景:员工涨工资时,新工资不能低于原工资(避免误操作),且部门平均工资不能超过公司规定的阈值(如10000元)。
案例代码:
CREATE OR REPLACE TRIGGER trg_salary_check
BEFORE INSERT OR UPDATE OF salary ON employees
FOR EACH ROW
DECLARE
v_dept_avg_sal NUMBER;
BEGIN
-- 检查新工资是否低于原工资
IF :NEW.salary < :OLD.salary THEN
RAISE_APPLICATION_ERROR(-20002, '新工资不能低于原工资(原工资:' || :OLD.salary || ')!');
END IF;
-- 计算所属部门的平均工资
SELECT AVG(salary)
INTO v_dept_avg_sal
FROM employees
WHERE department_id = :NEW.department_id;
-- 检查部门平均工资是否超过阈值
IF v_dept_avg_sal > 10000 THEN
RAISE_APPLICATION_ERROR(-20003, '部门(' || :NEW.department_id || ')平均工资已超过10000元,无法调整!');
END IF;
END;
/
说明:此触发器为BEFORE ROW类型,对employees表的salary列修改(插入或更新)逐行检查,使用:OLD(原值)和:NEW(新值)伪记录变量实现数据验证。
场景:需跟踪员工工资的变更历史(包括变更人、时间、新旧工资),便于后续审计。
案例代码:
-- 1. 创建审计日志表
CREATE TABLE emp_salary_audit (
audit_id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
emp_id NUMBER,
old_salary NUMBER,
new_salary NUMBER,
change_date TIMESTAMP DEFAULT SYSTIMESTAMP,
changed_by VARCHAR2(30) DEFAULT USER
);
-- 2. 创建触发器
CREATE OR REPLACE TRIGGER trg_emp_salary_audit
AFTER UPDATE OF salary ON employees
FOR EACH ROW
BEGIN
-- 将变更记录插入审计表
INSERT INTO emp_salary_audit (emp_id, old_salary, new_salary, changed_by)
VALUES (:OLD.employee_id, :OLD.salary, :NEW.salary, USER);
END;
/
说明:此触发器为AFTER ROW类型,在employees表salary列更新后执行,将变更信息插入emp_salary_audit表,实现完整的变更轨迹记录。
场景:employees表的department_id变更(如员工调岗)时,需自动更新对应departments表的employee_count(部门员工数),保持数据一致性。
案例代码:
-- 1. 确保departments表有employee_count字段
ALTER TABLE departments ADD (employee_count NUMBER DEFAULT 0);
-- 2. 创建触发器
CREATE OR REPLACE TRIGGER trg_sync_dept_employee_count
AFTER INSERT OR DELETE OR UPDATE OF department_id ON employees
FOR EACH ROW
BEGIN
-- 插入新员工:增加部门员工数
IF INSERTING THEN
UPDATE departments
SET employee_count = employee_count + 1
WHERE department_id = :NEW.department_id;
-- 删除员工:减少部门员工数
ELSIF DELETING THEN
UPDATE departments
SET employee_count = employee_count - 1
WHERE department_id = :OLD.department_id;
-- 更新部门ID:减少原部门数,增加新部门数
ELSIF UPDATING THEN
UPDATE departments
SET employee_count = employee_count - 1
WHERE department_id = :OLD.department_id;
UPDATE departments
SET employee_count = employee_count + 1
WHERE department_id = :NEW.department_id;
END IF;
END;
/
说明:此触发器为AFTER ROW类型,覆盖employees表的插入、删除、更新(department_id列)操作,通过IF-ELSIF分支实现不同场景下的部门员工数同步。
场景:需记录所有用户的登录和注销时间,便于监控数据库访问安全。
案例代码:
-- 1. 创建登录日志表
CREATE TABLE user_login_audit (
log_id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
username VARCHAR2(30),
action VARCHAR2(10), -- 'LOGIN' 或 'LOGOUT'
action_time TIMESTAMP DEFAULT SYSTIMESTAMP,
ip_address VARCHAR2(50)
);
-- 2. 创建登录触发器
CREATE OR REPLACE TRIGGER trg_log_user_login
AFTER LOGON ON DATABASE
BEGIN
INSERT INTO user_login_audit (username, action, ip_address)
VALUES (USER, 'LOGIN', SYS_CONTEXT('USERENV', 'IP_ADDRESS'));
END;
/
-- 3. 创建注销触发器
CREATE OR REPLACE TRIGGER trg_log_user_logout
BEFORE LOGOFF ON DATABASE
BEGIN
INSERT INTO user_login_audit (username, action, ip_address)
VALUES (USER, 'LOGOUT', SYS_CONTEXT('USERENV', 'IP_ADDRESS'));
END;
/
说明:此触发器为SYSTEM类型,分别监听AFTER LOGON(登录)和BEFORE LOGOFF(注销)事件,记录用户操作及IP地址,提升数据库安全性。
RAISE_APPLICATION_ERROR抛出明确错误信息,便于排查问题;避免静默失败导致数据不一致。