存储过程
CREATE OR REPLACE PROCEDURE raise_salary (
p_dept_id IN NUMBER,
p_percent IN NUMBER,
p_count OUT NUMBER,
p_total OUT NUMBER
) IS
BEGIN
IF p_percent <= 0 OR p_percent > 0.5 THEN
RAISE_APPLICATION_ERROR(-20001, '调薪比例必须在 0 到 0.5 之间');
END IF;
UPDATE employees
SET salary = salary * (1 + p_percent)
WHERE department_id = p_dept_id;
p_count := SQL%ROWCOUNT;
SELECT SUM(salary) INTO p_total
FROM employees
WHERE department_id = p_dept_id;
END raise_salary;
/只作示例阅读:5 行写入限制下只开放单独创建普通空表;CTAS、删表、清空表、改结构、触发器和存储程序不允许执行。请在独立学习数据库中练习。
-- 方式一:PL/SQL 块
DECLARE
v_cnt NUMBER;
v_sum NUMBER;
BEGIN
raise_salary(60, 0.05, v_cnt, v_sum);
DBMS_OUTPUT.PUT_LINE('调整 ' || v_cnt || ' 人,调整后部门工资总额 ' || v_sum);
ROLLBACK;
END;
/
-- 方式二:SQL*Plus 绑定变量
VARIABLE b_cnt NUMBER
VARIABLE b_sum NUMBER
EXEC raise_salary(60, 0.05, :b_cnt, :b_sum)
PRINT b_cnt b_sum
ROLLBACK函数
函数必须有返回值,可以在 SQL 语句里直接调用(过程不行)。但 SQL 中调用的函数不能读写本条语句正在操作的表、不能改包状态;若确实要写库,必须声明自治事务(PRAGMA AUTONOMOUS_TRANSACTION),否则运行期报 ORA-14551。
CREATE OR REPLACE FUNCTION years_of_service (
p_hire_date IN DATE
) RETURN NUMBER IS
v_years NUMBER;
BEGIN
IF p_hire_date IS NULL THEN
RETURN NULL;
END IF;
v_years := TRUNC(MONTHS_BETWEEN(SYSDATE, p_hire_date) / 12);
RETURN v_years;
END years_of_service;
/只作示例阅读:5 行写入限制下只开放单独创建普通空表;CTAS、删表、清空表、改结构、触发器和存储程序不允许执行。请在独立学习数据库中练习。
SELECT employee_id,
last_name,
hire_date,
years_of_service(hire_date) AS years
FROM employees
WHERE years_of_service(hire_date) >= 15
ORDER BY years DESC;注意:在 WHERE 里对列调用自定义函数会让该列上的索引失效(除非建函数索引)。数据量大时慎用。
只作示例阅读:受限模式不允许调用「YEARS_OF_SERVICE」:用户函数或程序可能在查询中写入数据。请在独立学习数据库中练习。
过程与函数怎么选
- 需要在 SQL 语句中调用 → 只能用函数
- 需要返回多个值 → 用过程加 OUT 参数,或返回游标
- 需要修改数据 → 优先用过程;函数要在 SQL 中写库必须用自治事务,并自行承担一致性后果
- 纯计算、无副作用 → 用函数,更容易复用和测试
- 逻辑复杂、多个程序共享 → 用包组织
包:把相关程序组织起来
- 包规范先编译,包体后编译;改包体不影响依赖对象的有效性
- 包内的私有过程/函数只在包体里声明,外部无法调用
- 包级变量在会话内保持状态,可用于缓存,但要注意会话隔离
-- 包规范:对外接口
CREATE OR REPLACE PACKAGE pkg_emp AS
g_min_salary CONSTANT NUMBER := 3000; -- 公共常量
FUNCTION get_avg_salary (p_dept_id IN NUMBER) RETURN NUMBER;
PROCEDURE adjust_salary (p_dept_id IN NUMBER, p_percent IN NUMBER);
END pkg_emp;
/
-- 包体:具体实现
CREATE OR REPLACE PACKAGE BODY pkg_emp AS
FUNCTION get_avg_salary (p_dept_id IN NUMBER) RETURN NUMBER IS
v_avg NUMBER;
BEGIN
SELECT ROUND(AVG(salary), 2) INTO v_avg
FROM employees WHERE department_id = p_dept_id;
RETURN NVL(v_avg, 0);
END get_avg_salary;
PROCEDURE adjust_salary (p_dept_id IN NUMBER, p_percent IN NUMBER) IS
BEGIN
UPDATE employees
SET salary = GREATEST(salary * (1 + p_percent), g_min_salary)
WHERE department_id = p_dept_id;
END adjust_salary;
END pkg_emp;
/
-- 调用
SELECT pkg_emp.get_avg_salary(60) FROM dual;只作示例阅读:5 行写入限制下只开放单独创建普通空表;CTAS、删表、清空表、改结构、触发器和存储程序不允许执行。请在独立学习数据库中练习。