Oracle存储过程与触发器的详细介绍

下面为你系统、清晰、实用 地介绍 Oracle 数据库中的 触发器(Trigger)存储过程(Stored Procedure) ,包括它们的概念、用途、语法、编写方法、注意事项 ,并附上可直接运行的示例代码


一、存储过程(Stored Procedure)

✅ 1. 什么是存储过程?

  • 存储过程是一组预编译的 SQL 语句和 PL/SQL 逻辑,存储在数据库中。
  • 可被应用程序多次调用,提高性能、减少网络传输、增强安全性。

✅ 2. 核心特点

特点 说明
可重复调用 一次编写,多次执行
参数化 支持 IN(输入)、OUT(输出)、IN OUT(双向)参数
事务控制 可包含 COMMIT / ROLLBACK(但通常由调用者控制)
权限封装 用户无需直接访问表,只需有执行存储过程的权限

✅ 3. 基本语法

sql 复制代码
CREATE [OR REPLACE] PROCEDURE procedure_name (
    param1 IN datatype,
    param2 OUT datatype,
    param3 IN OUT datatype
)
IS
    -- 声明局部变量(可选)
    v_count NUMBER;
BEGIN
    -- 执行逻辑:SQL + PL/SQL
    SELECT COUNT(*) INTO v_count FROM employees WHERE department_id = param1;
    
    IF v_count > 0 THEN
        param2 := '部门存在';
    ELSE
        param2 := '部门不存在';
    END IF;

EXCEPTION
    WHEN OTHERS THEN
        param2 := '错误: ' || SQLERRM;
        RAISE; -- 可选择是否抛出异常
END;
/

🔔 注意:

  • CREATE OR REPLACE:如果已存在则覆盖
  • /:在 SQL*Plus 或某些客户端中表示结束(PL/SQL 块结束符)

✅ 4. 调用存储过程

方式 1:在 PL/SQL 块中调用
sql 复制代码
DECLARE
    result VARCHAR2(100);
BEGIN
    check_dept_exists(10, result);  -- 调用
    DBMS_OUTPUT.PUT_LINE(result);
END;
/
方式 2:在 SQL*Plus 或 SQL Developer 中使用 EXEC
sql 复制代码
VAR msg VARCHAR2(100);
EXEC check_dept_exists(10, :msg);
PRINT msg;

✅ 5. 实用示例:插入员工并返回 ID

sql 复制代码
CREATE OR REPLACE PROCEDURE insert_employee (
    p_name      IN  VARCHAR2,
    p_salary    IN  NUMBER,
    p_dept_id   IN  NUMBER,
    p_emp_id    OUT NUMBER
)
IS
BEGIN
    INSERT INTO employees (name, salary, department_id)
    VALUES (p_name, p_salary, p_dept_id)
    RETURNING emp_id INTO p_emp_id;  -- 返回自增ID(假设emp_id是序列生成)

    COMMIT;  -- 注意:生产环境通常不在此提交
EXCEPTION
    WHEN DUP_VAL_ON_INDEX THEN
        p_emp_id := -1;
        ROLLBACK;
    WHEN OTHERS THEN
        p_emp_id := -2;
        ROLLBACK;
        RAISE;
END;
/

二、触发器(Trigger)

✅ 1. 什么是触发器?

  • 触发器是与表/视图/数据库事件关联的 PL/SQL 程序
  • 当特定事件(如 INSERTUPDATEDELETE)发生时,自动执行

✅ 2. 触发器类型

分类维度 类型 说明
触发时机 BEFORE / AFTER 在操作前或后执行
触发粒度 ROW / STATEMENT 每行触发 or 整条语句触发(默认是语句级)
触发事件 INSERT / UPDATE / DELETE 可组合,如 INSERT OR UPDATE
作用对象 表、视图、数据库(DDL/DCL) 最常用的是表级触发器

⚠️ 重要区别

  • 行级触发器 :用 FOR EACH ROW,可访问 :OLD:NEW 伪记录
  • 语句级触发器 :无 :OLD/:NEW,只执行一次

✅ 3. 基本语法(表级行触发器)

sql 复制代码
CREATE [OR REPLACE] TRIGGER trigger_name
    BEFORE | AFTER
    INSERT | UPDATE | DELETE
    ON table_name
    [FOR EACH ROW]
DECLARE
    -- 可声明变量
BEGIN
    -- 触发逻辑
    IF INSERTING THEN
        :NEW.created_time := SYSDATE;  -- 自动填充创建时间
    ELSIF UPDATING THEN
        :NEW.updated_time := SYSDATE;  -- 自动更新时间
    END IF;
EXCEPTION
    WHEN OTHERS THEN
        RAISE_APPLICATION_ERROR(-20001, '触发器错误: ' || SQLERRM);
END;
/

🔑 关键点:

  • :NEW.column:新值(INSERT/UPDATE 可写)
  • :OLD.column:旧值(UPDATE/DELETE 只读)
  • INSERTINGUPDATINGDELETING:布尔函数,判断当前操作类型

✅ 4. 实用示例

示例 1:自动审计日志(行级触发器)
sql 复制代码
-- 创建审计表
CREATE TABLE emp_audit (
    audit_id    NUMBER GENERATED ALWAYS AS IDENTITY,
    emp_id      NUMBER,
    action      VARCHAR2(10),  -- 'INSERT', 'UPDATE', 'DELETE'
    old_salary  NUMBER,
    new_salary  NUMBER,
    changed_by  VARCHAR2(30),
    changed_at  TIMESTAMP
);

-- 创建触发器
CREATE OR REPLACE TRIGGER trg_emp_audit
    AFTER INSERT OR UPDATE OR DELETE ON employees
    FOR EACH ROW
BEGIN
    IF INSERTING THEN
        INSERT INTO emp_audit (emp_id, action, new_salary, changed_by, changed_at)
        VALUES (:NEW.emp_id, 'INSERT', :NEW.salary, USER, SYSTIMESTAMP);
        
    ELSIF UPDATING THEN
        INSERT INTO emp_audit (emp_id, action, old_salary, new_salary, changed_by, changed_at)
        VALUES (:OLD.emp_id, 'UPDATE', :OLD.salary, :NEW.salary, USER, SYSTIMESTAMP);
        
    ELSIF DELETING THEN
        INSERT INTO emp_audit (emp_id, action, old_salary, changed_by, changed_at)
        VALUES (:OLD.emp_id, 'DELETE', :OLD.salary, USER, SYSTIMESTAMP);
    END IF;
END;
/
示例 2:防止工资降低(行级 BEFORE 触发器)
sql 复制代码
CREATE OR REPLACE TRIGGER trg_prevent_salary_cut
    BEFORE UPDATE OF salary ON employees
    FOR EACH ROW
BEGIN
    IF :NEW.salary < :OLD.salary THEN
        RAISE_APPLICATION_ERROR(-20002, '工资不能降低!');
    END IF;
END;
/

三、触发器 vs 存储过程:关键区别

特性 存储过程 触发器
调用方式 显式调用(CALL/EXEC) 隐式自动触发
参数 支持 IN/OUT 参数 无参数(通过 :OLD/:NEW 访问数据)
事务控制 可包含 COMMIT(不推荐) 不能 COMMIT/ROLLBACK(属于当前事务)
用途 业务逻辑封装 数据完整性、审计、自动维护
调试难度 较易 较难(自动执行,不易追踪)

💡 最佳实践

  • 存储过程 实现业务逻辑
  • 触发器 实现数据约束、审计、自动字段维护
  • 避免在触发器中写复杂逻辑(影响性能且难排查)

四、常见问题与注意事项

❗ 1. 触发器中的"变异表"错误(ORA-04091)

  • 原因:在行级触发器中查询正在被修改的表。
  • 解决方案
    • 使用 复合触发器(Compound Trigger)
    • 或将逻辑移到存储过程中,由应用调用

❗ 2. 性能影响

  • 触发器会增加 DML 操作的开销,避免在高频表上使用复杂触发器

❗ 3. 权限问题

  • 触发器以定义者权限(Definer's Rights) 运行(默认),不是调用者权限

❗ 4. 禁用/启用触发器

sql 复制代码
ALTER TRIGGER trg_emp_audit DISABLE;  -- 禁用
ALTER TRIGGER trg_emp_audit ENABLE;   -- 启用
ALTER TABLE employees DISABLE ALL TRIGGERS; -- 禁用表上所有触发器

✅ 总结

组件 核心价值 典型场景
存储过程 封装可复用业务逻辑 批量处理、API 接口、复杂计算
触发器 自动化数据维护 审计日志、自动时间戳、数据校验

🌟 记住

  • 存储过程 = 你主动调用的"函数"
  • 触发器 = 数据库自动执行的"监听器"

掌握这两者,你就拥有了在 Oracle 中实现高效、安全、自动化数据管理的核心能力。

相关推荐
2401_863318632 小时前
基于RS-232C的串口通信
数据库·mongodb
csdn_aspnet2 小时前
用 MySQL 玩转数据可视化:从底层数据到 BI 工具的桥接
数据库·mysql·信息可视化·bi
明洞日记2 小时前
【软考每日一练013】解析嵌入式网络数据库(NDB)架构
数据库·5g·嵌入式·软考·嵌入式实时数据库
wb043072012 小时前
一次jvm配置问题导致的数据库连接异常
服务器·jvm·数据库·后端
酷酷的崽7982 小时前
搭载cpolar,让PostgreSQL数据库远程访问超丝滑
数据库·postgresql
API开发2 小时前
apiSQL 迁移至已有 PostgreSQL 数据库指南
数据库·postgresql·api开发·postgrest·接口开发工具·api管理软件
学掌门2 小时前
从数据库到可视化性能,5个大数据分析工具测评,python只排倒数
数据库·python·数据分析
编程小风筝2 小时前
Django REST framework实现安全鉴权机制
数据库·安全·django
secondyoung2 小时前
队列原理与实现全解析
c语言·数据库·mysql·算法·队列