允许用户从动态 SQL 中选择

Posted

技术标签:

【中文标题】允许用户从动态 SQL 中选择【英文标题】:Allow users to SELECT from dynamic SQL 【发布时间】:2015-04-02 19:01:02 【问题描述】:

我一直在查看 Oracle 中的 DBMS_SQL 包,并试图查看是否有一种方法可以创建视图或用户可以从中选择以查看动态 SQL 查询的结果。

我有一个基于文档的测试程序:

CREATE OR REPLACE PROCEDURE test_dyn_sql AS
  cursor_name INTEGER;
  rows_processed INTEGER;
  l_query LONG;
BEGIN
  l_query := 'SELECT SYSDATE AS the_date, ''ABC'' AS the_string, 1 AS the_int FROM dual';  
  cursor_name := dbms_sql.open_cursor;
  DBMS_SQL.PARSE(cursor_name, l_query, DBMS_SQL.NATIVE);
  rows_processed := DBMS_SQL.EXECUTE(cursor_name);
  DBMS_SQL.CLOSE_CURSOR(cursor_name);      
EXCEPTION
  WHEN OTHERS THEN
    DBMS_SQL.CLOSE_CURSOR(cursor_name);
    DBMS_OUTPUT.put_line('ERROR');
END;

但这只是执行语句并且不返回任何内容。我想要的是一个视图,这样用户就可以执行SELECT the_date FROM some_view 并获得结果。我不会提前知道列的名称或数量,所以这就是我追求动态 SQL 解决方案的原因。

【问题讨论】:

我认为没有任何方法可以做到这一点。这不是视图的真正用途。您也许可以创建一个以某种方式返回表的函数?请参阅this 表函数示例。 【参考方案1】:

" 我不会提前知道名称或列数,所以这是 为什么我要使用动态 SQL 解决方案”

这在 SQL 中很难实现:SQL 都是关于数据结构的,它确实希望列存在于前面。因此,您不能在可变数据结构上构建 VIEW。

您可以实现一个返回引用光标的函数。这是一个指向可以由客户端解释的数据结构的指针,例如 JDBC ResultSet。

这是一个示例函数,它采用表名和列名,组合查询并返回其结果集。

CREATE OR REPLACE FUNCTION test_dyn_sql 
  (tab_name in varchar2
   , col_name in varchar2)
  return  sys_refcursor
AS
  return_value sys_refcursor;
BEGIN
  open return_value for
    'SELECT SYSDATE AS the_date, '||col_name||' FROM '||tab_name;  
  return return_value;
END;
/

SQL*Plus 中的输出不是很优雅,但你明白了。

SQL> select test_dyn_sql ('EMP', 'NAME') from dual;

TEST_DYN_SQL('EMP','
--------------------
CURSOR STATEMENT : 1

CURSOR STATEMENT : 1

THE_DATE  NAME
--------- ------------------------------
03-APR-15 FOX IN SOCKS
03-APR-15 MR KNOX
03-APR-15 DAISY-HEAD MAYZIE


SQL> 

我建议您尽可能坚持使用 Native Dynamic SQL(即execute immediate):正如您所看到的,与 DBMS_SQL 相比,它确实非常简单。仅当您有一些极其复杂的需求时才使用 DBMS_SQL。


我很感激这可能不是您正在寻找的解决方案。如果是这种情况,请编辑您的问题,以提供有关您要解决的问题的更多详细信息。

【讨论】:

【参考方案2】:

如果您希望您的代码返回 rows_processed 变量的值,请使用下面将返回 0 的代码。

CREATE OR REPLACE FUNCTION test_dyn_sql(asd INTEGER) RETURN INTEGER AS
      cursor_name INTEGER;
      rows_processed INTEGER;
      l_query LONG;
    BEGIN
      l_query := 'SELECT SYSDATE AS the_date, ''ABC'' AS the_string, 1 AS the_int FROM dual';  
      cursor_name := dbms_sql.open_cursor;
      DBMS_SQL.PARSE(cursor_name, l_query, DBMS_SQL.NATIVE);
      rows_processed := DBMS_SQL.EXECUTE(cursor_name);
      DBMS_SQL.CLOSE_CURSOR(cursor_name);      
      RETURN rows_processed;
    EXCEPTION
      WHEN OTHERS THEN
        DBMS_SQL.CLOSE_CURSOR(cursor_name);
        DBMS_OUTPUT.put_line('ERROR');
        RETURN 0;
    END;

这是从 plsql 调用函数的方式

DECLARE
  rows_precessed INTEGER;
BEGIN
  rows_precessed := test_dyn_sqll(0);
  DBMS_OUTPUT.put_line(rows_precessed);
END;

【讨论】:

以上是关于允许用户从动态 SQL 中选择的主要内容,如果未能解决你的问题,请参考以下文章

从 sql 中动态选择阈值

如何允许用户在他选择的位置拖动动态创建的控件

由于计数,db2/sql 查询不允许从连接中选择静态值

django - 表单中的动态选择字段

TSQL 从动态 sql 中选择到 Temp 表

如何从 Pentaho-Kettle 中基于动态选择的 sql 查询中获取结果集?