欢迎来到 Oracle PL/SQL 编程入门 的第二十一章!在这一章中,我们将深入探讨 包(Packages)。通过学习什么是包、如何创建和使用包以及如何扩展包,你将能够编写更加模块化和可维护的数据库操作代码。此外,我们还会介绍一些注意事项,并通过实际例子展示它们的用法。准备好迎接新的挑战了吗?让我们开始吧!
第一节:包
1.1 什么是包
包(Package) 是一组相关的 PL/SQL 对象(如存储过程、函数、游标等)的集合,它提供了封装和组织的功能。包分为两个部分:
- 包规范(Package Specification):定义包中的公共对象。
- 包体(Package Body):实现包规范中定义的对象。
小贴士:包就像是一个“工具箱”
包就像是给你的程序提供了一个“工具箱”,你可以把一系列相关的工具放在这个箱子里,然后只需要调用这个箱子的名字就能使用里面的工具。合理使用可以让程序更加模块化和易于维护。
第二节:包的创建
2.1 创建包
示例代码:
-- 创建包规范
CREATE OR REPLACE PACKAGE employee_pkg AS
-- 定义公共函数和过程
FUNCTION get_employee_count (p_department_id IN departments.department_id%TYPE) RETURN NUMBER;
PROCEDURE insert_employee (
p_employee_id IN employees.employee_id%TYPE,
p_first_name IN employees.first_name%TYPE,
p_last_name IN employees.last_name%TYPE
);
PROCEDURE update_employee_salary (
p_employee_id IN employees.employee_id%TYPE,
p_new_salary IN employees.salary%TYPE
);
END employee_pkg;
/
-- 创建包体
CREATE OR REPLACE PACKAGE BODY employee_pkg AS
-- 实现公共函数
FUNCTION get_employee_count (p_department_id IN departments.department_id%TYPE) RETURN NUMBER IS
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count
FROM employees
WHERE department_id = p_department_id;
RETURN v_count;
END get_employee_count;
-- 实现公共过程
PROCEDURE insert_employee (
p_employee_id IN employees.employee_id%TYPE,
p_first_name IN employees.first_name%TYPE,
p_last_name IN employees.last_name%TYPE
) IS
BEGIN
INSERT INTO employees (employee_id, first_name, last_name)
VALUES (p_employee_id, p_first_name, p_last_name);
DBMS_OUTPUT.PUT_LINE('已插入员工: ' || p_first_name || ' ' || p_last_name);
END insert_employee;
PROCEDURE update_employee_salary (
p_employee_id IN employees.employee_id%TYPE,
p_new_salary IN employees.salary%TYPE
) IS
BEGIN
UPDATE employees
SET salary = p_new_salary
WHERE employee_id = p_employee_id;
DBMS_OUTPUT.PUT_LINE('已更新员工ID: ' || p_employee_id || ' 的薪资为: ' || p_new_salary);
END update_employee_salary;
END employee_pkg;
/
执行说明:
- 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
- 创建一个名为
employee_pkg
的包,该包包含三个公共对象:一个函数和两个过程。
小贴士:包规范与包体分离让代码更清晰
包规范与包体分离就像是给你的程序提供了一个“蓝图”和“施工图”,让你可以更容易地理解和维护代码。合理使用可以让代码结构更加清晰。
2.2 创建私有对象
包中的私有对象只能在包体内访问,不能在包外直接调用。
示例代码:
-- 创建包规范
CREATE OR REPLACE PACKAGE employee_pkg AS
-- 定义公共函数和过程
FUNCTION get_employee_count (p_department_id IN departments.department_id%TYPE) RETURN NUMBER;
PROCEDURE insert_employee (
p_employee_id IN employees.employee_id%TYPE,
p_first_name IN employees.first_name%TYPE,
p_last_name IN employees.last_name%TYPE
);
PROCEDURE update_employee_salary (
p_employee_id IN employees.employee_id%TYPE,
p_new_salary IN employees.salary%TYPE
);
END employee_pkg;
/
-- 创建包体
CREATE OR REPLACE PACKAGE BODY employee_pkg AS
-- 私有函数
FUNCTION calculate_bonus (p_salary IN employees.salary%TYPE, p_bonus_rate IN NUMBER) RETURN NUMBER IS
BEGIN
RETURN p_salary * p_bonus_rate;
END calculate_bonus;
-- 实现公共函数
FUNCTION get_employee_count (p_department_id IN departments.department_id%TYPE) RETURN NUMBER IS
v_count NUMBER;
BEGIN
SELECT COUNT(*) INTO v_count
FROM employees
WHERE department_id = p_department_id;
RETURN v_count;
END get_employee_count;
-- 实现公共过程
PROCEDURE insert_employee (
p_employee_id IN employees.employee_id%TYPE,
p_first_name IN employees.first_name%TYPE,
p_last_name IN employees.last_name%TYPE
) IS
BEGIN
INSERT INTO employees (employee_id, first_name, last_name)
VALUES (p_employee_id, p_first_name, p_last_name);
DBMS_OUTPUT.PUT_LINE('已插入员工: ' || p_first_name || ' ' || p_last_name);
END insert_employee;
PROCEDURE update_employee_salary (
p_employee_id IN employees.employee_id%TYPE,
p_new_salary IN employees.salary%TYPE
) IS
v_bonus NUMBER;
BEGIN
v_bonus := calculate_bonus(p_new_salary, 0.1); -- 调用私有函数
UPDATE employees
SET salary = p_new_salary + v_bonus
WHERE employee_id = p_employee_id;
DBMS_OUTPUT.PUT_LINE('已更新员工ID: ' || p_employee_id || ' 的薪资为: ' || (p_new_salary + v_bonus));
END update_employee_salary;
END employee_pkg;
/
执行说明:
- 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
- 创建一个名为
employee_pkg
的包,该包包含一个私有函数calculate_bonus
和其他公共对象。
小贴士:私有对象让代码更安全
私有对象就像是给你的程序提供了一个“秘密武器库”,只有你自己知道里面有什么武器。合理使用可以让代码更加安全和可控。
2.3 调用存储包
示例代码:
BEGIN
-- 调用包中的过程
employee_pkg.insert_employee(999, 'John', 'Doe');
-- 调用包中的函数
DBMS_OUTPUT.PUT_LINE('部门ID 10 的员工数量: ' || employee_pkg.get_employee_count(10));
-- 调用包中的过程并更新薪资
employee_pkg.update_employee_salary(100, 5000);
END;
/
执行说明:
- 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
- 调用
employee_pkg
包中的过程和函数进行测试。 - 输出应为“已插入员工: John Doe”,“部门ID 10 的员工数量: X”,“已更新员工ID: 100 的薪资为: Y”。
小贴士:调用包让代码复用更方便
调用包就像是给你的程序提供了一个“快捷方式”,让你可以轻松地复用已经写好的代码。合理使用可以让代码复用更加方便和高效。
第三节:游标变量
3.1 什么是游标变量
游标变量(Cursor Variable) 是一种特殊的变量类型,它可以指向不同的查询结果集。游标变量可以在包中声明和使用。
示例代码:
-- 创建包规范
CREATE OR REPLACE PACKAGE cursor_pkg AS
TYPE emp_cursor_type IS REF CURSOR RETURN employees%ROWTYPE;
PROCEDURE open_cursor (p_dept_id IN departments.department_id%TYPE, p_cursor OUT emp_cursor_type);
END cursor_pkg;
/
-- 创建包体
CREATE OR REPLACE PACKAGE BODY cursor_pkg AS
PROCEDURE open_cursor (p_dept_id IN departments.department_id%TYPE, p_cursor OUT emp_cursor_type) IS
BEGIN
OPEN p_cursor FOR
SELECT * FROM employees
WHERE department_id = p_dept_id;
END open_cursor;
END cursor_pkg;
/
执行说明:
- 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
- 创建一个名为
cursor_pkg
的包,该包包含一个游标类型和一个过程。
小贴士:游标变量让数据处理更灵活
游标变量就像是给你的程序提供了一个“动态查询窗口”,让你可以根据需要动态地打开和关闭查询结果集。合理使用可以让数据处理更加灵活和强大。
3.2 如何使用游标变量
示例代码:
DECLARE
v_cursor cursor_pkg.emp_cursor_type;
v_employee employees%ROWTYPE;
BEGIN
-- 调用包中的过程打开游标
cursor_pkg.open_cursor(10, v_cursor);
-- 循环获取游标中的记录
LOOP
FETCH v_cursor INTO v_employee;
EXIT WHEN v_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('员工ID: ' || v_employee.employee_id || ', 名字: ' || v_employee.first_name || ' ' || v_employee.last_name);
END LOOP;
-- 关闭游标
CLOSE v_cursor;
END;
/
执行说明:
- 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
- 使用游标变量循环获取查询结果集,并输出员工信息。
- 输出应为每个员工的详细信息。
小贴士:游标变量让查询结果处理更灵活
游标变量就像是给你的程序提供了一个“动态查询引擎”,让你可以根据需要灵活地处理查询结果。合理使用可以让查询结果处理更加灵活和高效。
第四节:扩展包
4.1 什么是扩展包
扩展包(Extended Package) 是指通过继承或扩展现有包来添加新功能或修改已有功能的包。虽然 PL/SQL 不支持传统的面向对象编程中的继承,但可以通过重载函数和过程来实现类似的效果。
示例代码:
-- 创建扩展包规范
CREATE OR REPLACE PACKAGE extended_employee_pkg AS
-- 重载函数
FUNCTION get_employee_count (p_department_id IN departments.department_id%TYPE, p_include_inactive IN BOOLEAN DEFAULT FALSE) RETURN NUMBER;
-- 新增过程
PROCEDURE deactivate_employee (p_employee_id IN employees.employee_id%TYPE);
END extended_employee_pkg;
/
-- 创建扩展包体
CREATE OR REPLACE PACKAGE BODY extended_employee_pkg AS
-- 重载函数
FUNCTION get_employee_count (p_department_id IN departments.department_id%TYPE, p_include_inactive IN BOOLEAN DEFAULT FALSE) RETURN NUMBER IS
v_count NUMBER;
BEGIN
IF p_include_inactive THEN
SELECT COUNT(*) INTO v_count
FROM employees
WHERE department_id = p_department_id;
ELSE
SELECT COUNT(*) INTO v_count
FROM employees
WHERE department_id = p_department_id AND active_flag = 'Y';
END IF;
RETURN v_count;
END get_employee_count;
-- 新增过程
PROCEDURE deactivate_employee (p_employee_id IN employees.employee_id%TYPE) IS
BEGIN
UPDATE employees
SET active_flag = 'N'
WHERE employee_id = p_employee_id;
DBMS_OUTPUT.PUT_LINE('已停用员工ID: ' || p_employee_id);
END deactivate_employee;
END extended_employee_pkg;
/
执行说明:
- 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
- 创建一个名为
extended_employee_pkg
的扩展包,该包包含一个重载函数和一个新增的过程。
小贴士:扩展包让功能扩展更方便
扩展包就像是给你的程序提供了一个“升级插件”,让你可以轻松地添加新功能或修改已有功能。合理使用可以让功能扩展更加方便和高效。
4.2 使用扩展包
示例代码:
BEGIN
-- 调用扩展包中的重载函数
DBMS_OUTPUT.PUT_LINE('部门ID 10 的活跃员工数量: ' || extended_employee_pkg.get_employee_count(10));
DBMS_OUTPUT.PUT_LINE('部门ID 10 的所有员工数量: ' || extended_employee_pkg.get_employee_count(10, TRUE));
-- 调用扩展包中的过程
extended_employee_pkg.deactivate_employee(100);
END;
/
执行说明:
- 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
- 调用
extended_employee_pkg
包中的重载函数和过程进行测试。 - 输出应为“部门ID 10 的活跃员工数量: X”,“部门ID 10 的所有员工数量: Y”,“已停用员工ID: 100”。
小贴士:重载函数让接口设计更灵活
重载函数就像是给你的程序提供了一个“多功能工具”,让它可以根据不同的参数调用不同的实现。合理使用可以让接口设计更加灵活和用户友好。
第五节:包的安装和初始化
5.1 包的安装
包的安装是指将包部署到目标数据库环境中。通常情况下,只需执行创建包的脚本即可完成安装。
5.2 包的初始化
可以在包体中定义初始化部分,用于执行一些一次性操作,例如初始化全局变量或设置默认值。
示例代码:
-- 创建包规范
CREATE OR REPLACE PACKAGE init_pkg AS
FUNCTION get_default_salary RETURN NUMBER;
END init_pkg;
/
-- 创建包体
CREATE OR REPLACE PACKAGE BODY init_pkg AS
g_default_salary NUMBER := 3000; -- 全局变量
FUNCTION get_default_salary RETURN NUMBER IS
BEGIN
RETURN g_default_salary;
END get_default_salary;
-- 初始化部分
BEGIN
DBMS_OUTPUT.PUT_LINE('初始化包: 设置默认薪资为 ' || g_default_salary);
END init_pkg;
/
执行说明:
- 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
- 创建一个名为
init_pkg
的包,并在包体中定义初始化部分。 - 输出应为“初始化包: 设置默认薪资为 3000”。
小贴士:初始化部分让包启动更灵活
初始化部分就像是给你的包提供了一个“启动器”,让它可以在启动时执行一些必要的操作。合理使用可以让包启动更加灵活和高效。
第六节:SERIALLY_REUSABLE包属性
SERIALLY_REUSABLE 是一个包属性,表示包的状态是串行可重用的。这意味着包的状态在每次调用之间不会保留,适合用于无状态的操作。
示例代码:
-- 创建 SERIALLY_REUSABLE 包
CREATE OR REPLACE PACKAGE reusable_pkg AUTHID CURRENT_USER IS
PRAGMA SERIALLY_REUSABLE;
FUNCTION get_counter RETURN NUMBER;
END reusable_pkg;
/
CREATE OR REPLACE PACKAGE BODY reusable_pkg IS
g_counter NUMBER := 0;
FUNCTION get_counter RETURN NUMBER IS
BEGIN
g_counter := g_counter + 1;
RETURN g_counter;
END get_counter;
END reusable_pkg;
/
执行说明:
- 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
- 创建一个名为
reusable_pkg
的包,并设置其为SERIALLY_REUSABLE
。 - 每次调用
get_counter
函数时,计数器都会重置。
小贴士:SERIALLY_REUSABLE 让包状态管理更简单
SERIALLY_REUSABLE 属性就像是给你的包提供了一个“自动清理器”,让它可以在每次调用之间自动重置状态。合理使用可以让包状态管理更加简单和高效。
第七节:注意事项
在编写 PL/SQL 包时,有一些常见问题需要注意,以避免潜在的错误和性能问题。
7.1 错误处理
在包中,错误处理尤为重要。使用异常处理机制来捕获和处理可能的错误。
示例:
CREATE OR REPLACE PACKAGE error_pkg AS
PROCEDURE process_employee (p_employee_id IN employees.employee_id%TYPE);
END error_pkg;
/
CREATE OR REPLACE PACKAGE BODY error_pkg AS
PROCEDURE process_employee (p_employee_id IN employees.employee_id%TYPE) IS
BEGIN
UPDATE employees
SET salary = salary * 1.1
WHERE employee_id = p_employee_id;
IF SQL%ROWCOUNT = 0 THEN
RAISE_APPLICATION_ERROR(-20001, '未找到指定的员工ID');
END IF;
DBMS_OUTPUT.PUT_LINE('已更新员工ID: ' || p_employee_id || ' 的薪资');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('发生错误: ' || SQLERRM);
END process_employee;
END error_pkg;
/
执行说明:
- 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
- 如果更新成功,返回员工的薪资;如果失败,输出相应的错误信息。
小贴士:异常处理让程序更健壮
异常处理就像是给你的程序提供了一个“救生圈”,让它可以在遇到错误时及时采取措施,确保程序的稳定性。合理使用可以让程序更加健壮和可靠。
第八节:代码挑战
现在是时候来挑战一下自己了!我们将通过几个简单的练习来巩固所学的知识。
挑战一:创建一个简单的包
编写一个 PL/SQL 包,包含两个函数:一个用于计算员工的奖金,另一个用于查询员工的薪资。然后调用这些函数进行测试。
示例代码:
-- 创建包规范
CREATE OR REPLACE PACKAGE employee_pkg AS
-- 计算员工奖金
FUNCTION calculate_bonus (
p_salary IN employees.salary%TYPE,
p_bonus_rate IN NUMBER
) RETURN NUMBER;
-- 查询员工薪资
FUNCTION get_employee_salary (
p_employee_id IN employees.employee_id%TYPE
) RETURN NUMBER;
END employee_pkg;
/
-- 创建包体
CREATE OR REPLACE PACKAGE BODY employee_pkg AS
-- 实现计算员工奖金的函数
FUNCTION calculate_bonus (
p_salary IN employees.salary%TYPE,
p_bonus_rate IN NUMBER
) RETURN NUMBER IS
BEGIN
RETURN p_salary * p_bonus_rate;
END calculate_bonus;
-- 实现查询员工薪资的函数
FUNCTION get_employee_salary (
p_employee_id IN employees.employee_id%TYPE
) RETURN NUMBER IS
v_salary employees.salary%TYPE;
BEGIN
SELECT salary INTO v_salary
FROM employees
WHERE employee_id = p_employee_id;
RETURN v_salary;
END get_employee_salary;
END employee_pkg;
/
-- 调用包中的函数
DECLARE
v_bonus NUMBER;
v_salary NUMBER;
BEGIN
-- 调用 calculate_bonus 函数
v_bonus := employee_pkg.calculate_bonus(5000, 0.1);
DBMS_OUTPUT.PUT_LINE('计算出的奖金: ' || v_bonus);
-- 调用 get_employee_salary 函数
v_salary := employee_pkg.get_employee_salary(100);
DBMS_OUTPUT.PUT_LINE('员工ID 100 的薪资: ' || v_salary);
END;
/
执行说明:
- 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
- 创建一个名为
employee_pkg
的包,包含两个函数:calculate_bonus
和get_employee_salary
。 - 调用这两个函数并输出结果。
- 输出应为“计算出的奖金: 500”和“员工ID 100 的薪资: X”。
小贴士:包让代码更模块化
包就像是给你的程序提供了一个“工具箱”,让你可以把相关的功能封装在一起。合理使用可以让代码更加模块化和易于维护。
挑战二:创建一个带游标变量的包
编写一个 PL/SQL 包,包含一个函数,该函数返回一个游标变量,用于查询部门中的所有员工信息。然后调用这个函数进行测试。
示例代码:
-- 创建包规范
CREATE OR REPLACE PACKAGE department_pkg AS
-- 定义游标类型
TYPE emp_cursor_type IS REF CURSOR;
-- 返回部门中的所有员工信息
FUNCTION get_employees_by_department (
p_department_id IN departments.department_id%TYPE
) RETURN emp_cursor_type;
END department_pkg;
/
-- 创建包体
CREATE OR REPLACE PACKAGE BODY department_pkg AS
-- 实现返回部门中的所有员工信息的函数
FUNCTION get_employees_by_department (
p_department_id IN departments.department_id%TYPE
) RETURN emp_cursor_type IS
emp_cursor emp_cursor_type;
BEGIN
OPEN emp_cursor FOR
SELECT employee_id, first_name, last_name, salary
FROM employees
WHERE department_id = p_department_id;
RETURN emp_cursor;
END get_employees_by_department;
END department_pkg;
/
-- 调用包中的函数
DECLARE
v_emp_cursor department_pkg.emp_cursor_type;
v_employee_id employees.employee_id%TYPE;
v_first_name employees.first_name%TYPE;
v_last_name employees.last_name%TYPE;
v_salary employees.salary%TYPE;
BEGIN
-- 调用 get_employees_by_department 函数
v_emp_cursor := department_pkg.get_employees_by_department(10);
-- 循环读取游标数据
LOOP
FETCH v_emp_cursor INTO v_employee_id, v_first_name, v_last_name, v_salary;
EXIT WHEN v_emp_cursor%NOTFOUND;
-- 输出员工信息
DBMS_OUTPUT.PUT_LINE('员工ID: ' || v_employee_id || ', 名字: ' || v_first_name || ' ' || v_last_name || ', 薪资: ' || v_salary);
END LOOP;
-- 关闭游标
CLOSE v_emp_cursor;
END;
/
执行说明:
- 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
- 创建一个名为
department_pkg
的包,包含一个函数get_employees_by_department
,该函数返回一个游标变量。 - 调用这个函数并输出结果。
- 输出应为部门ID为10的所有员工的信息。
小贴士:游标变量让数据处理更灵活
游标变量就像是给你的程序提供了一个“动态指针”,让你可以根据需要遍历查询结果。合理使用可以让数据处理更加灵活和高效。
挑战三:创建一个扩展包
编写一个 PL/SQL 包,包含一个函数用于计算员工的总薪酬(包括基本薪资和佣金)。然后创建一个扩展包,添加一个新的函数用于计算员工的年度总薪酬(包括基本薪资、佣金和奖金)。最后调用这些函数进行测试。
示例代码:
-- 创建基础包规范
CREATE OR REPLACE PACKAGE base_employee_pkg AS
-- 计算员工总薪酬
FUNCTION calculate_total_compensation (
p_employee_id IN employees.employee_id%TYPE
) RETURN NUMBER;
END base_employee_pkg;
/
-- 创建基础包体
CREATE OR REPLACE PACKAGE BODY base_employee_pkg AS
-- 实现计算员工总薪酬的函数
FUNCTION calculate_total_compensation (
p_employee_id IN employees.employee_id%TYPE
) RETURN NUMBER IS
v_salary employees.salary%TYPE;
v_commission_pct employees.commission_pct%TYPE;
v_total_compensation NUMBER;
BEGIN
-- 查询员工的基本薪资和佣金比例
SELECT salary, commission_pct
INTO v_salary, v_commission_pct
FROM employees
WHERE employee_id = p_employee_id;
-- 计算总薪酬
IF v_commission_pct IS NOT NULL THEN
v_total_compensation := v_salary + (v_salary * v_commission_pct);
ELSE
v_total_compensation := v_salary;
END IF;
RETURN v_total_compensation;
END calculate_total_compensation;
END base_employee_pkg;
/
-- 创建扩展包规范
CREATE OR REPLACE PACKAGE extended_employee_pkg AS
-- 继承基础包
SUBTYPE base_package IS base_employee_pkg;
-- 计算员工年度总薪酬
FUNCTION calculate_annual_total_compensation (
p_employee_id IN employees.employee_id%TYPE
) RETURN NUMBER;
END extended_employee_pkg;
/
-- 创建扩展包体
CREATE OR REPLACE PACKAGE BODY extended_employee_pkg AS
-- 实现计算员工年度总薪酬的函数
FUNCTION calculate_annual_total_compensation (
p_employee_id IN employees.employee_id%TYPE
) RETURN NUMBER IS
v_total_compensation NUMBER;
v_bonus NUMBER;
BEGIN
-- 调用基础包中的函数计算总薪酬
v_total_compensation := base_package.calculate_total_compensation(p_employee_id);
-- 计算奖金
v_bonus := v_total_compensation * 0.1;
-- 计算年度总薪酬
RETURN v_total_compensation + v_bonus;
END calculate_annual_total_compensation;
END extended_employee_pkg;
/
-- 调用包中的函数
DECLARE
v_total_compensation NUMBER;
v_annual_total_compensation NUMBER;
BEGIN
-- 调用 calculate_total_compensation 函数
v_total_compensation := base_employee_pkg.calculate_total_compensation(100);
DBMS_OUTPUT.PUT_LINE('员工ID 100 的总薪酬: ' || v_total_compensation);
-- 调用 calculate_annual_total_compensation 函数
v_annual_total_compensation := extended_employee_pkg.calculate_annual_total_compensation(100);
DBMS_OUTPUT.PUT_LINE('员工ID 100 的年度总薪酬: ' || v_annual_total_compensation);
END;
/
执行说明:
- 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
- 创建一个名为
base_employee_pkg
的基础包,包含一个函数calculate_total_compensation
。 - 创建一个名为
extended_employee_pkg
的扩展包,继承基础包并添加一个新函数calculate_annual_total_compensation
。 - 调用这两个函数并输出结果。
- 输出应为“员工ID 100 的总薪酬: X”和“员工ID 100 的年度总薪酬: Y”。
小贴士:扩展包让功能扩展更简单
扩展包就像是给你的程序提供了一个“升级插件”,让你可以在不修改原有代码的情况下添加新功能。合理使用可以让功能扩展更加简单和高效。
第九节:本章总结
在这一章中,我们深入探讨了 PL/SQL 中的包(Packages),包括什么是包、如何建立和使用包以及如何扩展和优化包的性能。以下是本章的主要内容总结:
总结要点:
- 包 是一组相关存储过程、函数和其他对象的集合,可以提高代码的模块化和重用性。
- 创建包 包括创建包规范和包体。包规范定义了包的接口,而包体实现了包的功能。
- 游标变量 允许你在包中返回查询结果集,适用于复杂的查询场景。
- 扩展包 可以基于现有包进行扩展,增加新的功能而不影响原有功能。
- SERIALLY_REUSABLE 标志可以使包的状态在会话之间共享,适用于特定的应用场景。
恭喜你完成了第二十一章的学习!通过这节课,你已经掌握了 包 的使用方法,并为后续更高级的内容打下了坚实的基础。未来的学习中,我们将继续探索 PL/SQL 的更多功能,如异常处理、事务控制等。希望你能继续保持对 PL/SQL 的兴趣,勇敢探索,成为一名熟练的 PL/SQL 用户!