从 Oracle 中的动态 SQL 获取结果集中的结果

Posted

技术标签:

【中文标题】从 Oracle 中的动态 SQL 获取结果集中的结果【英文标题】:Getting results in a result set from dynamic SQL in Oracle 【发布时间】:2010-04-05 17:45:25 【问题描述】:

这个问题与我在 *** 上找到的其他几个问题相似,但差异对我来说足以证明一个新问题,所以这里是:

我想从Oracle中的动态SQL获取结果集,然后在类似SqlDeveloper的工具中显示为结果集,就像我直接执行了动态SQL语句一样。这在 SQL Server 中很简单,所以具体来说,这是一个来自 SQL Server 的示例,它在 SQL Server Management Studio 或查询资源管理器中返回结果集:

EXEC sp_executesql N'select * from countries'

或者更准确地说:

DECLARE @stmt nvarchar(100)
SET @stmt = N'select * from countries'
EXEC sp_executesql @stmt

问题"How to return a resultset / cursor from a Oracle PL/SQL anonymous block that executes Dynamic SQL?" 解决了问题的前半部分——将动态SQL 执行到游标中。问题"How to make Oracle procedure return result sets" 提供了类似的答案。网络搜索揭示了同一主题的许多变体,所有这些都只解决了我的问题的前半部分。我发现this post 解释了如何在 SqlDeveloper 中执行此操作,但这使用了 SqlDeveloper 的一些功能。我实际上正在使用自定义查询工具,因此我需要在 SQL 代码中独立包含该解决方案。此自定义查询工具同样无法显示打印 (dbms_output.put_line) 语句的输出;它只显示结果集。这里还有one more possible avenue 使用'execute immediate...bulk collect',但这个示例再次使用 dbms_output.put_line 语句循环呈现结果。 This link 试图解决这个话题,但这个问题也没有得到很好的回答。

假设这是可能的,我将再添加一个条件:我想这样做而不必定义函数或过程(由于数据库权限有限)。也就是说,我想执行一个包含动态 SQL 的自包含 PL/SQL 块,并在 SqlDeveloper 或类似工具中返回一个结果集。


总结一下:

我想执行任意 SQL 语句(因此是动态 SQL)。 平台是 Oracle。 解决方案必须是没有过程或函数的 PL/SQL 块。 必须将输出生成为规范的结果集;没有打印语句。 输出必须在 SqlDeveloper 中呈现为结果集,而不使用任何 SqlDeveloper 特殊功能。

有什么建议吗?

【问题讨论】:

【参考方案1】:

我能想到的最接近的事情是创建一个需要权限的动态视图。这肯定会涉及使用一个 PL/SQL 块一个 SQL 查询而不是过程/函数。但是,任何动态查询都可以从结果网格中转换和查看,因为它将作为选择查询运行。

DEFINE view_name = 'my_results_view';
SET FEEDBACK OFF
SET ECHO OFF
DECLARE
  l_view_name VARCHAR2(40)     := '&view_name';
  l_query     VARCHAR2(4000)   := 'SELECT 1+level as id,
                                  ''TEXT''||level as text  FROM DUAL ';
  l_where_clause VARCHAR2(4000):= 
                           ' WHERE TRUNC(1.0) =  1 CONNECT BY LEVEL < 10';
BEGIN
     EXECUTE IMMEDIATE 'CREATE OR REPLACE VIEW '
                       || l_view_name
                       || ' AS '
                       || l_query
                       || l_where_clause;
END;
/
 select * from &view_name;

【讨论】:

【参考方案2】:

您似乎在要求一段 PL/SQL 代码,该代码将采用任意查询返回未确定结构的结果集,并以某种方式“转发/重组”该结果集,以便某些“自定义 GUI 工具”。

如果是这样,请查看 DBMS_SQL 以获取动态 SQL。它有一个 DESCRIBE_COLUMNS 过程,该过程从动态 SELECT 语句返回列。您需要的步骤是,

    解析语句 描述结果集(列名和数据类型) 获取每一行和每一列,调用数据类型相关函数以将该值返回到局部变量中 将这些局部变量放入已定义的结构中以返回调用环境(例如一致的列名 [如 col_1、col_2] 可能都是 VARCHAR2)

作为替代方案,您可以尝试将查询构建到 XMLFOREST 语句中,然后从 XML 中解析结果。


添加: 与 SQL Server 不同,Oracle PL/SQL 调用不会“自然”返回单个结果集。它可以打开一个或多个引用游标并将它们传回给客户端。然后,从这些引用游标中获取记录和列成为客户端的责任。如果您的客户不能/不能处理这个问题,那么您就不能使用 PL/SQL 调用。 存储函数可以返回一个预定义的集合类型,它可以让您执行类似“select * from table(func_name('select * from countries'))”的操作。但是,该函数不能执行 DML(更新/删除/插入/合并),因为它消除了该查询的任何一致性概念。加上返回的结构是固定的,因此

select * from table(func_name('select * from countries'))

必须返回与

相同的列集(列名和数据类型)
select * from table(func_name('select * from persons'))

使用 DBMS_SQL 或 XMLFOREST,这样的函数可以进行动态查询并将其重组为一组预定义的列(col_1、col_2 等),以便以一致的方式返回。但我看不出这有什么意义。

【讨论】:

感谢您的意见,加里。我对 DBMS_SQL 或 XMLFOREST 都不熟悉,所以我都读了一点。也许我遗漏了一些东西,但我不相信这两种技术都能满足我的要求。关于您的 DBMS_SQL 建议,我看到 2 个问题。首先,它似乎需要提前了解目标语句中的内容。其次,它没有让我得到像 SQL Server 示例这样的简单结果集。一个澄清:是的,我使用的是自定义 GUI 工具,但解决方案必须首先使用 PL/SQL 代码在 SqlDeveloper 中工作,我看不出你提到的任何一种技术如何做到这一点。【参考方案3】:

试试试试这些。

DECLARE
  TYPE EmpCurTyp  IS REF CURSOR;
  v_emp_cursor    EmpCurTyp;
  emp_record      employees%ROWTYPE;
  v_stmt_str      VARCHAR2(200);
  v_e_job         employees.job%TYPE;
BEGIN
  -- Dynamic SQL statement with placeholder:
  v_stmt_str := 'SELECT * FROM employees WHERE job_id = :j';

  -- Open cursor & specify bind argument in USING clause:
  OPEN v_emp_cursor FOR v_stmt_str USING 'MANAGER';

  -- Fetch rows from result set one at a time:
  LOOP
    FETCH v_emp_cursor INTO emp_record;
    EXIT WHEN v_emp_cursor%NOTFOUND;
  END LOOP;

  -- Close cursor:
  CLOSE v_emp_cursor;
END;


declare
  v_rc    sys_refcursor;
begin
   v_rc := get_dept_emps(10);  -- This returns an open cursor
   dbms_output.put_line('Rows: '||v_rc%ROWCOUNT);
   close v_rc;
end;

在此处查找更多示例。 http://forums.oracle.com/forums/thread.jspa?threadID=886365&tstart=0

【讨论】:

谢谢,但是这个提议的解决方案违反了我的摘要中的第 4 点(没有打印声明)和第 5 点。这两个都是必需的,因为如上所述,我正在使用自定义 GUI 工具来查看我的结果(将其视为一个非常基本的 SqlDeveloper 应用程序)。【参考方案4】:

在 TOAD 中执行以下脚本时,系统会提示您输入 v_result 的类型。从类型选择列表中选择光标,结果随后显示在 Toad 的数据网格中(类似结果的 excel 电子表格)。也就是说,当使用游标作为结果时,您应该始终编写两个程序(客户端和服务器)。在这种情况下,“TOAD”将是客户端。

DECLARE
   v_result      sys_refcursor;
   v_dynamic_sql   VARCHAR2 (4000);
BEGIN
   v_dynamic_sql := 'SELECT * FROM user_objects where ' || ' 1 = 1';

   OPEN :v_result FOR (v_dynamic_sql);
END;

Oracle 的 SQL Developer 中可能也有类似的机制来提示绑定。

【讨论】:

有一个类似的机制,正如我在我的问题中用一个链接指出的那样,但这个建议违反了我总结中的第 5 点(没有特殊的 SqlDeveloper 功能——代码块需要完全自包含)。

以上是关于从 Oracle 中的动态 SQL 获取结果集中的结果的主要内容,如果未能解决你的问题,请参考以下文章

Java SQL 异常 - r.getString 没有从结果集中获取列

是否有从结果集中构建插入语句的 Oracle SQL 工具?

无法从 Java 中的结果集中获取日期值

如何从java中的结果集中获取列名[重复]

如何从 Java 中的 Oracle SQL 选择中获取原始脚本输出而不是查询结果

从 Oracle SQL 查询中获取结果