从 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 工具?