如何在 SQL 中检索 PL/SQL 记录类型? [复制]

Posted

技术标签:

【中文标题】如何在 SQL 中检索 PL/SQL 记录类型? [复制]【英文标题】:How to retrieve PL/SQL record type in SQL? [duplicate] 【发布时间】:2018-01-27 04:18:51 【问题描述】:

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

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

好吧,废话不多说,这是我的实际问题——调用返回记录类型并在 SQL 中选择/操作结果的 PL/SQL 函数的最直接方法是什么?

【问题讨论】:

也许这个链接会对你有所帮助,***.com/questions/1020348/… 我的个人(!)感觉是:您经常在学生书籍和教程中看到 Oracle 对象类型,它们是许多 PL/SQL 培训中的一个重要主题。然而,实际上对象类型几乎不用于生产应用程序。 “几乎”并不意味着“从不”——但我只是觉得它们在教育中比在生产中处理得更多。 @eifla001 在更仔细地阅读了您链接的问题之后,它似乎相当完整地回答了我的问题。我不知道为什么,在我所有的搜索中,我自己从来没有找到这个问题。希望欺骗目标可以帮助下一个人找到它。 【参考方案1】:

杀手线in the documentation就是这个:

PL/SQL 函数不能将 PL/SQL-only 类型的值返回给 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 权限的用户尝试使用该函数时会发生什么?

【讨论】:

+1,谢谢,这是有道理的。那么,12c 中的“更多 PL/SQL-Only 数据类型可以跨 PL/SQL-to-SQL 接口”是什么意思? Steven Feuerstein 发布了 LiveSQL 演示,展示了这些变化带来的新事物。 Find it here

以上是关于如何在 SQL 中检索 PL/SQL 记录类型? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 12 PL/SQL 在触发器中检索存储过程名称

PL/SQL-如何使用游标的所有列插入表

插入行类型记录,该记录在Oracle中的PL / SQL中作为参数传递。我该如何实现?

如何根据表名作为输入在 PL/Sql 中动态创建记录

在 oracle pl/sql 中如何选择嵌套类型?

oracle pl/sql如何定义变量