Oracle 存储过程(Stored Procedure)是一种存储在数据库中的可重用程序单元,它可以接受输入参数、执行一系列操作(如数据查询、更新、事务处理等),但不直接返回值(通过 OUT 或 IN OUT 参数输出结果)。存储过程常用于封装复杂业务逻辑、实现数据处理流程或批量操作。
存储过程由三部分组成:
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;
/
-- 创建存储过程
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);
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;
存储过程的参数有三种模式:
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;
PL/SQL 块中调用:
BEGIN
add_employee(1002, 'Jane', 'Smith', 6000);
END;
SQL*Plus 或命令行中调用:
EXECUTE add_employee(1002, 'Jane', 'Smith', 6000);
-- 或简化为:
EXEC add_employee(1002, 'Jane', 'Smith', 6000);
通过应用程序调用:使用 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;
/
特性 | 存储过程 | 存储函数 |
---|---|---|
返回值 | 无返回值(通过 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 存储过程是数据库编程的核心工具,适合封装复杂业务逻辑、实现事务控制和批量操作。合理使用存储过程可以提高应用性能、增强数据安全性和简化维护工作。在设计存储过程时,需注意参数模式、异常处理和事务控制,确保过程健壮且高效。