前几次我们针对表·函数进行了说明。

表函数是像表一样可以查询的行的集合的返回函数。
可以在SELECT语句的FROM语句后边,作为一个表进行查询。

还有一个经常的用法就是,该表函数作为游标变量当做函数的一个参数进行使用。
具体来说,将其他查询(SELECT语句)的结果用表函数的形式进行使用。
在表函数中,通过游标变量参照其他查询的结果,根据该值进行某个程序处理,
结果通过PIPE ROW处理从表函数中返回数据。

例如,为了说明极端简单的例子,有以下的查询(SELECT*FROM TEST02)。

SELECT * FROM TEST02
A B C


1 EMP 7934
2 DEPT 10

TEST02表的查询结果有两行数据。

这里的第一行数据,B列为“EMP”,C列的值是EMP表(员工表)的主键EMPNO列(员工编号)的值。
第二行数据:B列为“DEPT”时,C列的值为DEPT表(部门表)的主键DEPTNO列(部门编号)。

根据上面的查询结果,追加D列:
如果EMP的话,D列就设置成职员名
如果是DEPT的话,D列就设置部门名。
这时候我们可以使用表函数达到我们的目的。

根据上面的查询结果,用表函数转换的查询结果例

A B C D–原来的查询中追加了D列


1 EMP 7934 MILLER --MILLER是员工编号7934的员工名
2 DEPT 10 ACCOUNTING --ACCOUNTING是部门编号10的部门名称

那么我们用PAC2这个包,创建这个功能的表函数F2。

例子用用户SCOTT来创建这个包。

首先,是PAC2包的申明部的创建。尽量简单。

CREATE OR REPLACE PACKAGE PAC2
IS
//
/表函数行的记录类型/
/
/
TYPE REC_TYPE IS RECORD
( A VARCHAR2(20),
B VARCHAR2(20),
C VARCHAR2(20),
D VARCHAR2(20));

/*************************************************/
/*对应记录类型的配列变量*/
/*************************************************/
TYPE REC_TAB_TYPE IS TABLE OF REC_TYPE;
/**************************************************/

/*返回配列的表函数*/
/**************************************************/
FUNCTION F2 (P_CURSOR IN SYS_REFCURSOR)
RETURN REC_TAB_TYPE PIPELINED;

END PAC2;
/

包已创建。

首先创建了包申明部。

F2的参数是P_CURSOR,它的类型为SYS_REFCURSOR,这是游标变量的类型。

在第46章中,介绍了游标变量的定义。它分为两个阶段:。

①TYPE 类型名称 IS REF CURSOR;
(※IS REF CURSOR固定)
②游标变量名称 上述①的类型名称;

这2个阶段可以简化为一个阶段来定义和使用。就是直接用SYS_REFCURSOR关键字,可以更容易地定义游标变量。

函数F2的参数是游标变量,所以函数F2的参数可以是其各种查询结果的行。

接下来是包的主体。

CREATE OR REPLACE PACKAGE BODY PAC2
IS
//
/表函数F2的定义/
/
/
FUNCTION F2 (P_CURSOR IN SYS_REFCURSOR)
RETURN REC_TAB_TYPE PIPELINED
IS
–表函数的行记录
REC REC_TYPE;

--游标行的A列、B列、C列变量
V_A VARCHAR2(20);
V_B VARCHAR2(20);
V_C VARCHAR2(20);

--添加的D列变量
V_D VARCHAR2(20);

BEGIN
LOOP --重复以下处理
FETCH P_CURSOR INTO V_A,V_B,V_C;–从游标变量里取得1行数据
EXIT WHEN P_CURSOR%NOTFOUND;–如果没有取得的话就结束重复

--将取得的各列的值设置在表函数的行记录中
    REC.A := V_A;
    REC.B := V_B;
    REC.C := V_C;

    --根据C列的值,取到D列得值,D列的值可从EMP表或DEPT表中获取
    IF V_B = 'EMP' THEN
        SELECT ENAME INTO V_D FROM EMP WHERE EMPNO = V_C;
    ELSE
        SELECT DNAME INTO V_D FROM DEPT WHERE DEPTNO = V_C;
    END IF;

    REC.D := V_D;
    PIPE ROW(REC);--通过管道返回行记录

END LOOP;

RETURN;--上述重复处理结束后函数结束

END F2;

END PAC2;
/

包主体创建完成。

请注意的是,对游标变量P_CURSOR不进行OPEN处理而是直接进行的FETCH处理。
在第46章里边介绍了,游标变量和显式游标一样,OPEN后才可以进行FETCH处理。
但是,这里的例子该游标在调用方被OPEN了,所以这里不需要OPEN处理。
同样,CLOSE处理也是在表函数结束后也会自动调用,所以这里不需要显示的调用CLOSE。◆◆

游标变量的循环结束的判定一般是通过游标变量%NOTFOUND进行的。
没有取得的行的时候,循环处理结束。

D列取到并设置值之后,用PIPE ROW处理返回该行记录。
因为这里的处理尽可能的简单,没有记述例外处理等。

我们试用一下吧:

SELECT * FROM TABLE (PAC2.F2( CURSOR ( SELECT * FROM TEST02)));
A B C D


1 EMP 7934 MILLER
2 DEPT 10 ACCOUNTING

这里需要注意的是,PAC2.F2表行数的参数是“CURSOR(SELECT * FROM TEST02)”。
这是把“SELECT*FROM TEST02”的数据行作为参数传递给向PAC2.F2表函数使用。

语法结构总结如下:

SELECT * FROM TABLE(表函数(CURSOR(SELECT语句));

以这样的形式,我们就可以对CURSOR进行二次查询,对游标的结果也进行了变换,得到变换后的结果。

本次转换的例子是针对1行数据生成新的成1行数据的例子。
也可以通过类似方式吧原来1行的数据生成多行数据,反之,原来多行的数据生成1行数据,这些都是可以做到的。

另外,表函数不是将新做成的所有数据保存下来,最后将其全部返回的,
而是通过PIPE ROW处理,每次生成一行时就及时返回。因此,没有数据的中间保存的处理逻辑。

此外,还可以通过表函数对表函数的结果进行转换和处理。
也就是说表函数可以连锁使用。

因此,在分段进行大量数据转换的时候这种方式性能较好。
※大量数据转的时候,高性能的处理方式可以使用并行处理和Oracle Streams等进行处理,详情请参阅相关手册。

本章到此为止。