Oracle PL/SQL 编程入门:第二十一章 包 Packages

欢迎来到 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;
/

执行说明:

  1. 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
  2. 创建一个名为 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;
/

执行说明:

  1. 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
  2. 创建一个名为 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;
/

执行说明:

  1. 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
  2. 调用 employee_pkg 包中的过程和函数进行测试。
  3. 输出应为“已插入员工: 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;
/

执行说明:

  1. 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
  2. 创建一个名为 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;
/

执行说明:

  1. 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
  2. 使用游标变量循环获取查询结果集,并输出员工信息。
  3. 输出应为每个员工的详细信息。

小贴士:游标变量让查询结果处理更灵活

游标变量就像是给你的程序提供了一个“动态查询引擎”,让你可以根据需要灵活地处理查询结果。合理使用可以让查询结果处理更加灵活和高效。


第四节:扩展包

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;
/

执行说明:

  1. 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
  2. 创建一个名为 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;
/

执行说明:

  1. 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
  2. 调用 extended_employee_pkg 包中的重载函数和过程进行测试。
  3. 输出应为“部门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;
/

执行说明:

  1. 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
  2. 创建一个名为 init_pkg 的包,并在包体中定义初始化部分。
  3. 输出应为“初始化包: 设置默认薪资为 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;
/

执行说明:

  1. 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
  2. 创建一个名为 reusable_pkg 的包,并设置其为 SERIALLY_REUSABLE
  3. 每次调用 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;
/

执行说明:

  1. 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
  2. 如果更新成功,返回员工的薪资;如果失败,输出相应的错误信息。

小贴士:异常处理让程序更健壮

异常处理就像是给你的程序提供了一个“救生圈”,让它可以在遇到错误时及时采取措施,确保程序的稳定性。合理使用可以让程序更加健壮和可靠。


第八节:代码挑战

现在是时候来挑战一下自己了!我们将通过几个简单的练习来巩固所学的知识。

挑战一:创建一个简单的包

编写一个 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;
/

执行说明:

  1. 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
  2. 创建一个名为 employee_pkg 的包,包含两个函数:calculate_bonusget_employee_salary
  3. 调用这两个函数并输出结果。
  4. 输出应为“计算出的奖金: 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;
/

执行说明:

  1. 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
  2. 创建一个名为 department_pkg 的包,包含一个函数 get_employees_by_department,该函数返回一个游标变量。
  3. 调用这个函数并输出结果。
  4. 输出应为部门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;
/

执行说明:

  1. 在 SQL*Plus 或 SQL Developer 中运行上述代码块。
  2. 创建一个名为 base_employee_pkg 的基础包,包含一个函数 calculate_total_compensation
  3. 创建一个名为 extended_employee_pkg 的扩展包,继承基础包并添加一个新函数 calculate_annual_total_compensation
  4. 调用这两个函数并输出结果。
  5. 输出应为“员工ID 100 的总薪酬: X”和“员工ID 100 的年度总薪酬: Y”。

小贴士:扩展包让功能扩展更简单

扩展包就像是给你的程序提供了一个“升级插件”,让你可以在不修改原有代码的情况下添加新功能。合理使用可以让功能扩展更加简单和高效。


第九节:本章总结

在这一章中,我们深入探讨了 PL/SQL 中的包(Packages),包括什么是包、如何建立和使用包以及如何扩展和优化包的性能。以下是本章的主要内容总结:

总结要点:

  1. 是一组相关存储过程、函数和其他对象的集合,可以提高代码的模块化和重用性。
  2. 创建包 包括创建包规范和包体。包规范定义了包的接口,而包体实现了包的功能。
  3. 游标变量 允许你在包中返回查询结果集,适用于复杂的查询场景。
  4. 扩展包 可以基于现有包进行扩展,增加新的功能而不影响原有功能。
  5. SERIALLY_REUSABLE 标志可以使包的状态在会话之间共享,适用于特定的应用场景。

恭喜你完成了第二十一章的学习!通过这节课,你已经掌握了 的使用方法,并为后续更高级的内容打下了坚实的基础。未来的学习中,我们将继续探索 PL/SQL 的更多功能,如异常处理、事务控制等。希望你能继续保持对 PL/SQL 的兴趣,勇敢探索,成为一名熟练的 PL/SQL 用户!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

caifox菜狐狸

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值