如何在SQL中检索PL / SQL记录类型?

时间:2018-01-27 04:18:52

标签: sql oracle plsql oracle12c user-defined-types

存在一个共享包,它定义学生记录类型和返回学生的函数:

CREATE OR REPLACE PACKAGE shared.student_utils IS
  --aggregates related data from several tables
  TYPE student_rec IS RECORD (
    id       student.id%TYPE,
    username student.username%TYPE,
    name     student.name%TYPE,
    phone    phone.phone%TYPE
    /*etc.*/
  );

  FUNCTION get_student(student_id IN student.id%TYPE) RETURN student_rec;
END;

现在,我正在编写一个为Apex应用程序提供API的软件包。特别是,我需要以可以通过SQL选择的格式提供学生记录和其他相关数据(并显示在Apex的报告页面中。)

到目前为止,我一直在努力寻找在SQL中选择数据的最直接方法。显然,记录类型不能在SQL中使用,所以我的快速和肮脏的想法是在我的包规范中定义一个表类型,并在我的包规范/正文中定义PIPELINED函数:

CREATE OR REPLACE PACKAGE my_schema.api IS
  TYPE student_tab IS TABLE OF shared.student_utils.student_rec;

  FUNCTION get_all_student_data(student_id IN student.id%TYPE) RETURN student_tab PIPELINED;
END;
/
CREATE OR REPLACE PACKAGE BODY my_schema.api IS
  FUNCTION get_all_student_data(student_id IN student.id%TYPE) RETURN student_tab PIPELINED IS
  BEGIN
    PIPE ROW(shared.student_utils.get_student(student_id));
  END;
END;

...让我像这样选择它:

SELECT * FROM TABLE(my_schema.api.get_all_student_data(1234));

这很有效,但是为一行构建一个流水线表是过度的,Oracle的解释计划似乎也同意。

据说在Oracle 12c中,应该有更多可用选项:

  

More PL/SQL-Only Data Types Can Cross PL/SQL-to-SQL Interface

...但我似乎无法在我的场景中弄明白。将功能更改为:

FUNCTION get_all_student_data RETURN student_tab IS
  r_student_tab student_tab;
BEGIN
  r_student_tab(1) := shared.student_utils.get_student(student_id);

  RETURN r_student_tab;
END;

...将编译,但我不能像以前那样SELECT

好的,漫游,这是我的实际问题 - 调用PL / SQL函数的最直接方法是什么,它返回记录类型并在SQL中选择/操作结果?

1 个答案:

答案 0 :(得分:3)

杀手线in the documentation就是这个:

  

PL / SQL函数不能将PL / SQL专用类型的值返回给SQL。

这似乎排除了直接从返回PL / SQL记录或关联数组的函数查询,如下所示:

select * from table(student_utils.get_students(7890));

这是什么工作,这在技术上是SQL(因为文档将匿名块定义为SQL而不是PL / SQL):

declare
    ltab student_utils.students_tab;
    lrec student_utils.student_rec;
    rc sys_refcursor;
begin
    ltab := student_utils.get_students(1234);
    open rc for select * from table(ltab);
    fetch rc into lrec;
    dbms_output.put_line(lrec.name);
    close rc;
end;
/

这是相当蹩脚的。我想有几次我们想要从数组中打开一个引用游标,而不是仅仅为我们用来填充数组的SQL打开它,但它不是最紧迫的用例。

问题在于Oracle的内部架构:内核有用于SQL的C模块和用于PL / SQL的C模块(这就是为什么你会听到人们在谈论"上下文切换")。向SQL引擎公开更多PL / SQL功能需要修改接口。我们只能想象允许SQL编译器对PL / SQL数据结构的定义起作用是多么困难,这些定义非常不稳定(每次运行create or replace package ...时它们都可能会改变。

当然这适用于PIPELINED函数,但这是因为在引擎盖下Oracle为函数中引用的PL / SQL类型创建SQL类型。对于我们可能希望进入table()调用的任意函数,它无法动态创建对象。有一天它可能是可能的,但只考虑一个关键点:当我们的包上执行但缺乏CREATE TYPE权限的用户尝试使用该功能时会发生什么?