Oralce方言下Package专题

  其他常见问题
内容纲要

一. Package的概念

在一个大型项目中,可能有很多模块,而每个模块又有自己的过程、函数等。而这些过程、函数默认是放在一起的(如在PL/SQL中,过程默认都是放在一起 的,即Procedures中),这些非常不方便查询和维护,甚至会发生误删除的事件。

PL/SQL为了满足程序模块化的需要,引入了包的构造。通过使用包就可以分类管理过程和函数等。

  1. 包是一种数据库对象,相当于一个容器。将逻辑上相关的过程、函数、变量、常量和游标组合成一个更大的单位。用户可以从其他 PL/SQL 块中对其进行引用
  2. 包类似于C++和JAVA语言中的类,其中变量相当于类中的成员变量,过程和函数相当于类方法。把相关的模块归类成为包,可使开发人员利用面向对象的方法进行开发,具有面向对象程序设计语言的特点,
  3. PL/SQL的包具有信息隐蔽性(information hiding),仅在算法和数据结构设计有关层可见。可将过程说明和过程体组成一个程序单位。也可将过程说明与它的过程体分开。也可在包中定义过程,而该过程在包说明中没有定义过程说明,这样定义的过程仅在包内使用。
  4. 在PL/SQL程序设计中,使用包不仅可以使程序设计模块化,对外隐藏包内所使用的信息(通过使用私用变量),而写可以提高程序的执行效率。

优点:

  1. 把函数和存储过程进行分门别类,不同package的存储过程可以重名,可以有效避免命名冲突和加强模块化管理
  2. 被组合在一起的相关对象的集合,当包中任何函数或者存储过程被调用,包就会被加载入内存,包中的任何函数或存储过程的子程序访问速度将大大加快。

一个完整的Package包括包头(specification)和包体(body)两个部分

二. Package的创建

  1. 只有当包头编辑成功后才能编辑包体.其中的函数名与过程名须和包头中的函数过程一样.
  2. 包说明和包体必须有相同的名字
  3. 包的开始没有begin语句,与存储过程和函数不同。
  4. 在包的说明部分定义函数和过程的名称和参数,具体实现在包体中定义。
  5. 在包内声明常量、变量、类型定义、异常、及游标时不使用declare。
  6. 包内的过程和函数的定义不要create or replace语句。
  7. 包声明和包体两者分离。

1. 语法

1.1 创建包头

CREATE [OR REPLACE] PACKAGE package_name
{IS | AS}
    [公有数据类型定义]
    [公有游标声明]
    [公有变量、常量声明]
    [公有子程序声明]
END;

A. 包头中直接定义的变量,均为全局变量,可以在包外使用。
B. 包头中仅声明过程,函数,RECORD,Collections等的类型

1.2 创建包体

CREATE [OR REPLACE] PACKAGE BODY package_name
{IS | AS}
    [私有数据类型定义]
    [私有变量、常量声明]
    [私有子程序声明和定义]
    [公有子程序定义]
BEGIN
    执行部分(初始化部分)
END;

A. 包体中直接定义的变量均为局部变量,只能在包内使用
B. 包体中具体定义过程,函数,RECORD类型的变量等

1.3 包的调用

Package-name.type-name
Package-name.procedure-name
Package-name.function-name
...

Package-name 是程序包名称,
type-name是包内声明的类型名称,
procedure-name是包内声明的过程名称,
function-name 是包内声明的函数名称

2. 实例

2.1 创建包头

-- 创建一个名为test的包头。
CREATE OR REPLACE PACKAGE test
IS
    -- 定义一个名为transid的全局变量,类型为string。
    transid STRING
    -- 定义一个名为l_row的全局变量,类型为int。
    l_row int := 1
    -- 声明一个名为test_procedure的过程,形参名为test_name,类型为字符串。
    -- 声明一个名为aa_type的NESTED TABLE类型,用来存放整数型的数据。
    TYPE aa_type IS TABLE OF INTEGER
END;
/

2.2 创建包体

-- 声明一个名为test的包体,此处必须与包头名一致。
CREATE OR REPLACE PACKAGE body test
IS
    -- 具体定义过程test_procedure的内容。
    PROCEDURE test_procedure(test_name string) IS
    BEGIN
        dbms_output.put_line(test_name ||'是个好同学!')
    END
END;
/

2.3 包的调用

DECLARE
    -- 声明一个包内NESTED TABLE类型aa_type的变量v_aa_type。
    v_aa_type test.aa_type:=test.aa_type()
BEGIN
    -- 给变量v_aa_type分配空间。
    v_aa_type.extend(3)
    -- 依次给变量v_aa_type赋值。
    v_aa_type(1) := '1001'
    v_aa_type(2) := '2001'
    v_aa_type(3):= '3001'
    -- 依次打印出变量v_aa_type里的值。
    FOR i IN 1..v_aa_type.count()
    LOOP
        dbms_output.put_line(v_aa_type(i))
    END LOOP;
    test.test_procedure('张三')
END;
/

三. Package的调用

Inceptor中,在创建Packages的时候可以仅创建包头不用再创建包体;但是创建包体的时候,一定要有一个相同名字的包头的存在。

1. 仅创建包头

1.1 创建一个名为aa_pkg的包头,在包内定义一个名为aa_type的表类型

-- 创建一个名为aa_pkg的包头。
CREATE OR REPLACE PACKAGE aa_pkg IS
    -- 创建包内类型NESTED TABLE类型,名为aa_type。
    TYPE aa_type IS TABLE OF INTEGER
END;
/

1.2 创建一个名为print_aa的过程,依次打印出表类型变量中的值

CREATE OR REPLACE PROCEDURE print_aa (aa aa_pkg.aa_type) IS
-- 定义一个整数类型的变量i。
i INT
BEGIN
    -- 使用集合元素方法FIRST()和NEXT(i),依次打印出aa内的值。
    i := aa.FIRST()
    WHILE i IS NOT NULL LOOP
        DBMS_OUTPUT.PUT_LINE (i ||' '||aa(i))
        i := aa.NEXT(i)
    END LOOP;
END;
/

1.3 创建一个aa_pkg.aa_type类型的变量为aa_var

-- 给变量aa_var分配大小为3的空间。
-- 依次给变量aa_var赋值。
-- 利用过程print_aa打印出变量的值。
!set plsqlUseSlash true
DECLARE
    -- 创建一个类型为aa_pkg包内类型aa_type的变量aa_var,并初始化。
    aa_var aa_pkg.aa_type:=aa_pkg.aa_type()
BEGIN
    aa_var.extend(3)
    aa_var(1) := '1001'
    aa_var(2) := '2001'
    aa_var(3):= '3001'
    print_aa(aa_var)
END;
/

2. 创建包头和包体

2.1 创建一个名为testpackage的包头

-- 创建一个名为testpackage的包头。
CREATE OR REPLACE PACKAGE testpackage
IS
    -- 声明一个名为record_type的记录类型,分量名分别为trans_time,price,分量类型分别为string,double。
    TYPE record_type IS RECORD(trans_time STRING, price DOUBLE)
    -- 声明一个名为cur的动态游标。
    TYPE cur IS REF CURSOR
    -- 声明一个名为testprocedure()的过程,且不带参数。
    PROCEDURE testprocedure()
END;
/

2.2 创建一个名为testpackage的包体

-- 创建一个名为testpackage的包体,此处和包头名相同。
CREATE OR REPLACE PACKAGE BODY testpackage
IS
    -- 定义一个类型为record_type的变量v_record_type。
    v_record_type record_type
    -- 定义一个游标类型为cur的游标变量sqlcur。
    sqlcur cur
    -- 具体定义过程testprocedure()的内容。
    PROCEDURE testprocedure() IS
BEGIN
    -- 打开游标sqlcur,去查询表transactions中交易号为943197522的交易时间和价格。
    OPEN sqlcur FOR
        SELECT trans_time,price     FROM transactions WHERE trans_id=943197522;
    LOOP
        -- 将游标查询到的结果放进变量v_record_type里。
        FETCH sqlcur INTO v_record_type
            -- 如果游标没有查询到结果,就退出。
            EXIT WHEN sqlcur%notfound
            -- 输出变量v_record_type的分量trans_time和分量price的值。
            dbms_output.put_line(v_record_type.trans_time||' '||v_record_type.price)
    END LOOP;
    -- 关闭游标。
    CLOSE sqlcur
    END;
END;
/

2.3 调用包内函数

BEGIN
    -- 调用包内过程testpackage.testprocedure(),此处不可以直接调用testprocedure()。
    testpackage.testprocedure()
END;
/

四.Inceptor预定义包

1.dbms_output

Inceptor中,包dbms_output是系统预定义的,包内只有一个名为put_line的过程,且put_line过程也是系统预定义的。

在实际的使用中,可以直接调用过程put_line打印结果,也可以调用包内过程put_line来打印结果。

BEGIN
    FOR test IN 1..5 LOOP
        dbms_output.put_line('test:'||test)
    END LOOP;
END;
/

2.owa_util

在撰写ETL调度工具时,在往日志表里面写调度日志时,需要写入当前执行的用户名称、执行作业的名称等信息,所以就想到了OWA_UTIL.WHO_CALLED_ME这个过程。

Parameter Description
owner The owner of the program unit.–程序单元的所有者。
name The name of the program unit. This is the name of the package, if the calling program unit is wrapped in a package, or the name of the procedure or function if the calling program unit is a standalone procedure or function. If the calling program unit is part of an anonymous block, this is NULL.–程序单元的名称。这是包的名称(如果调用程序单元包装在包中),或者是过程或函数的名称(如果调用程序单元是独立的过程或函数)。如果调用程序单元是匿名块的一部分,则该值为NULL。
lineno The line number within the program unit where the call was made.–调用所在程序单元中的行号。
caller_t The type of program unit that made the call. The possibilities are: package body, anonymous block, procedure, and function. Procedure and function are only for standalone procedures and functions.–发出调用的程序单元的类型。可能性有:包体、匿名块、过程和函数。过程和函数仅用于独立的过程和函数。这一点很重要,如果过程在包里面,返回的是包名。
-- 创建一个名为outer_proc()的过程。
CREATE OR REPLACE PROCEDURE outer_proc()
IS
    -- 分别声明四个变量。
    v_owner STRING
    v_name STRING
    v_lineno INT
    v_type STRING
BEGIN
    DBMS_OUTPUT.PUT_LINE('Outer outer proc')
    -- 调用包内过程owa_util.who_called_me,其中v_owner, v_name, v_lineno,
    v_type分别用来储存相对应形参所获得的值。
    owa_util.who_called_me(v_owner, v_name, v_lineno, v_type)
    -- 依次输出四个变量的值。
    DBMS_OUTPUT.PUT_LINE('Owner: ' || v_owner)
    DBMS_OUTPUT.PUT_LINE('Name: ' || v_name)
    DBMS_OUTPUT.PUT_LINE('Lineno: ' || v_lineno)
    DBMS_OUTPUT.PUT_LINE('Type: ' || v_type)
END;
/
BEGIN
outer_proc()
END;
/

file

通过结果来看,own_util包形同虚设

这篇文章对您有帮助吗?

平均评分 0 / 5. 次数: 0

尚无评价,您可以第一个评哦!

非常抱歉,这篇文章对您没有帮助.

烦请您告诉我们您的建议与意见,以便我们改进,谢谢您。