Oracle 存储过程

Oracle 存储过程(Stored Procedure)是一种存储在数据库中的可重用程序单元,它可以接受输入参数、执行一系列操作(如数据查询、更新、事务处理等),但不直接返回值(通过 OUT 或 IN OUT 参数输出结果)。存储过程常用于封装复杂业务逻辑、实现数据处理流程或批量操作。

存储过程的基本结构

存储过程由三部分组成:

  1. 过程头:声明过程名称和参数。
  2. 声明部分:定义局部变量、游标等。
  3. 执行部分:包含业务逻辑和事务控制。

CREATE [OR REPLACE] PROCEDURE procedure_name (
    parameter1 [IN | OUT | IN OUT] datatype1,
    parameter2 [IN | OUT | IN OUT] datatype2,
    ...
)
IS
    -- 声明部分(可选)
    variable1 datatype;
    variable2 datatype;
BEGIN
    -- 执行部分
    -- 业务逻辑...
    COMMIT;  -- 提交事务(如果需要)
EXCEPTION
    -- 异常处理(可选)
    WHEN exception_type THEN
        -- 处理异常...
        ROLLBACK;  -- 回滚事务(如果需要)
END;
/

存储过程的创建与使用示例

示例 1:简单过程(插入员工记录)
-- 创建存储过程
CREATE OR REPLACE PROCEDURE add_employee (
    p_emp_id NUMBER,
    p_first_name VARCHAR2,
    p_last_name VARCHAR2,
    p_salary NUMBER
)
IS
BEGIN
    INSERT INTO employees (employee_id, first_name, last_name, salary)
    VALUES (p_emp_id, p_first_name, p_last_name, p_salary);
    
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('员工添加成功!');
EXCEPTION
    WHEN DUP_VAL_ON_INDEX THEN
        DBMS_OUTPUT.PUT_LINE('错误:员工ID已存在!');
        ROLLBACK;
END;
/

-- 调用存储过程
EXEC add_employee(1001, 'John', 'Doe', 5000);
示例 2:带输出参数的过程(查询员工信息)
CREATE OR REPLACE PROCEDURE get_employee_info (
    p_emp_id IN NUMBER,
    p_salary OUT NUMBER,
    p_department OUT VARCHAR2
)
IS
BEGIN
    SELECT salary, department_name
    INTO p_salary, p_department
    FROM employees e
    JOIN departments d ON e.department_id = d.department_id
    WHERE employee_id = p_emp_id;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        p_salary := 0;
        p_department := '未知部门';
END;
/

-- 调用存储过程(在PL/SQL块中)
DECLARE
    v_salary NUMBER;
    v_department VARCHAR2(100);
BEGIN
    get_employee_info(100, v_salary, v_department);
    DBMS_OUTPUT.PUT_LINE('工资:' || v_salary);
    DBMS_OUTPUT.PUT_LINE('部门:' || v_department);
END;

存储过程的参数模式

存储过程的参数有三种模式:

  1. IN(默认):输入参数,调用时必须传入值,过程内部不能修改。
  2. OUT:输出参数,用于返回值,调用前无需赋值,过程内部必须赋值。
  3. IN OUT:输入输出参数,调用时需传入值,过程内部可修改并返回。
CREATE OR REPLACE PROCEDURE swap_values (
    p_num1 IN OUT NUMBER,
    p_num2 IN OUT NUMBER
)
IS
    v_temp NUMBER;
BEGIN
    v_temp := p_num1;
    p_num1 := p_num2;
    p_num2 := v_temp;
END;
/

-- 调用示例
DECLARE
    v_a NUMBER := 10;
    v_b NUMBER := 20;
BEGIN
    swap_values(v_a, v_b);
    DBMS_OUTPUT.PUT_LINE('交换后:a=' || v_a || ', b=' || v_b);  -- 输出:a=20, b=10
END;

存储过程的调用方式

  1. PL/SQL 块中调用

    BEGIN
        add_employee(1002, 'Jane', 'Smith', 6000);
    END;
    
  2. SQL*Plus 或命令行中调用

    EXECUTE add_employee(1002, 'Jane', 'Smith', 6000);
    -- 或简化为:
    EXEC add_employee(1002, 'Jane', 'Smith', 6000);
    
  3. 通过应用程序调用:使用 JDBC、ODBC 等接口从外部程序调用。

// Java 通过 JDBC 调用存储过程示例
CallableStatement cstmt = conn.prepareCall("{call add_employee(?, ?, ?, ?)}");
cstmt.setInt(1, 1002);
cstmt.setString(2, "Jane");
cstmt.setString(3, "Smith");
cstmt.setDouble(4, 6000);
cstmt.execute();

存储过程的异常处理

使用 EXCEPTION 块捕获和处理异常:

CREATE OR REPLACE PROCEDURE update_salary (
    p_emp_id NUMBER,
    p_amount NUMBER
)
IS
BEGIN
    UPDATE employees
    SET salary = salary + p_amount
    WHERE employee_id = p_emp_id;
    
    IF SQL%NOTFOUND THEN
        RAISE_APPLICATION_ERROR(-20001, '员工ID不存在!');
    END IF;
    
    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('错误:' || SQLERRM);
        ROLLBACK;
END;
/

存储过程的事务控制

存储过程中可使用 COMMIT 和 ROLLBACK 控制事务:

CREATE OR REPLACE PROCEDURE transfer_funds (
    p_from_account NUMBER,
    p_to_account NUMBER,
    p_amount NUMBER
)
IS
BEGIN
    -- 扣款
    UPDATE accounts
    SET balance = balance - p_amount
    WHERE account_id = p_from_account;
    
    -- 存款
    UPDATE accounts
    SET balance = balance + p_amount
    WHERE account_id = p_to_account;
    
    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;
        RAISE_APPLICATION_ERROR(-20002, '转账失败:' || SQLERRM);
END;
/

存储过程的应用场景

  1. 封装业务逻辑:将复杂业务规则封装在存储过程中,确保数据处理一致性。
  2. 批量操作:执行大量数据的插入、更新或删除,减少网络传输开销。
  3. 事务控制:在一个过程中完成多个操作,保证数据完整性。
  4. 安全性:通过存储过程限制对表的直接访问,增强数据安全。
  5. 性能优化:减少客户端与数据库之间的交互次数。

存储过程的优缺点

优点
  • 提高性能:预编译后执行效率高,减少网络通信。
  • 可维护性:集中管理业务逻辑,便于修改和维护。
  • 安全性:通过权限控制,限制对底层表的直接访问。
  • 事务完整性:确保一组操作的原子性。
缺点
  • 可移植性差:不同数据库的存储过程语法差异较大。
  • 调试复杂:调试工具相对有限,调试过程较繁琐。
  • 扩展性有限:大规模应用中,过度依赖存储过程会导致数据库负载过高。

存储过程与存储函数的区别

特性 存储过程 存储函数
返回值 无返回值(通过 OUT 参数输出) 必须返回单个值
调用方式 通过 EXECUTE 或 PL/SQL 调用 可嵌入 SQL 语句中
适用场景 执行操作(如插入数据、批量更新) 计算和返回值
SQL 集成 不能直接在 SQL 中调用 可作为表达式在 SQL 中使用

示例:复杂存储过程(批量更新员工工资)

CREATE OR REPLACE PROCEDURE bulk_update_salaries (
    p_department_id NUMBER,
    p_percentage NUMBER,
    p_updated_count OUT NUMBER
)
IS
BEGIN
    UPDATE employees
    SET salary = salary * (1 + p_percentage/100)
    WHERE department_id = p_department_id;
    
    p_updated_count := SQL%ROWCOUNT;  -- 获取受影响的行数
    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        p_updated_count := 0;
        ROLLBACK;
END;
/

-- 调用示例
DECLARE
    v_updated_count NUMBER;
BEGIN
    bulk_update_salaries(10, 5, v_updated_count);
    DBMS_OUTPUT.PUT_LINE('更新了 ' || v_updated_count || ' 名员工的工资');
END;

总结

Oracle 存储过程是数据库编程的核心工具,适合封装复杂业务逻辑、实现事务控制和批量操作。合理使用存储过程可以提高应用性能、增强数据安全性和简化维护工作。在设计存储过程时,需注意参数模式、异常处理和事务控制,确保过程健壮且高效。

你可能感兴趣的:(oracle,数据库,大数据开发)