空 Oracle REF CURSOR 中的列名

Posted

技术标签:

【中文标题】空 Oracle REF CURSOR 中的列名【英文标题】:Column names in an empty Oracle REF CURSOR 【发布时间】:2011-07-01 07:29:59 【问题描述】:

在 PL/SQL 中,我可以在这里使用类似这样的技巧从 REF CURSOR 中找出一行中每一列的名称/值对:

TO_CHAR of an Oracle PL/SQL TABLE type

这是一个很棒的技巧。但是当REF CURSOR为空时不起作用,比如这里的这个(这只是一个例子。真正的光标不是从DUAL中选择的):

OPEN cursor FOR SELECT 1 FROM DUAL WHERE 1 = 0;

空的REF CURSOR 是否还有列名/类型信息?

【问题讨论】:

它可能被称为 COLUMN_VALUE。 @Steve:总是?我实际上不是从DUAL 中选择的。这只是一个例子 好的,根据我的经验,未命名的列被命名为 COLUMN_VALUE。不确定这是否适用于您的情况,或者我只是在找错树。 @Lukas,如果游标中没有要处理的行,我不确定您期望从 printCur 过程输出什么信息?你到底是什么意思“它不起作用”?可能我误会你了 @tbone,当我通过 JDBC 执行SELECT 1 AS one FROM DUAL WHERE 1=0 时,我实际上在ResultSet 元数据中得到了列名one,即使没有行。但从REF CURSOR 获取的ResultSet 并非如此。所以我实际上想知道这是否是 PL/SQL 级别的问题 【参考方案1】:

是的,我已经尝试过没有行的解决方案,你是对的。 从我有限的角度来看,我认为这里我们需要两种不同的方法来检索列的名称和值。

1) 用于检索列名的 Dbms_sql 包。

2) tbone 方法来检索数据。


程序

create or replace procedure demo(sqlText in varchar2) is
    refCur sys_refcursor;
    curId  integer;
    cnt    number;
    ret    dbms_sql.desc_tab;
    recTab dbms_sql.desc_tab;
    FORMAT_STRING constant pls_integer := 20;

    procedure printDescTab(desctab in sys.dbms_sql.desc_tab) is
    begin
        -- do what you want with the columns
        for i in 1 .. desctab.count
        loop
            dbms_output.put(lpad(desctab(i).col_name, FORMAT_STRING));
        end loop;
        dbms_output.new_line;
    end printDescTab;

    procedure PrintCur(cv in sys_refcursor) is
    begin
        for c in ( --select t2.COLUMN_VALUE.getrootelement() name,
                  select EXTRACTVALUE(t2.COLUMN_VALUE, 'node()') value
                  from   table(XMLSEQUENCE(cv)) t
                         ,table(XMLSEQUENCE(EXTRACT(COLUMN_VALUE, '/ROW/node()'))) t2)
        loop
            DBMS_OUTPUT.put(lpad(c.VALUE, FORMAT_STRING));
        end loop;
        dbms_output.new_line;
        dbms_output.new_line;
    end;

begin
    dbms_output.put_line('dynamic sql: ' || sqlText);
    curId := dbms_sql.open_cursor();
    --  checks for sql injection to do...
    dbms_sql.parse(curId, sqlText, dbms_sql.native);
    dbms_sql.describe_columns(curId, cnt, recTab);
    printDescTab(recTab);
    dbms_sql.close_cursor(curId);

    open refCur for sqlText;
    PrintCur(refCur);
    close refCur;
exception
    when others then
        if dbms_sql.is_open(curId) then
           dbms_sql.close_cursor(curId);
        end if;
        if refCur%isopen then
            close RefCur;
        end if;
        dbms_output.put_line(sqlcode || ' - ' || sqlerrm);
end demo;

测试

declare
    sqlText varchar2(2000);
begin
    sqlText := 'select 1 as one, 2 as two  from  dual where 1=0';
    demo(sqlText);
    sqlText   := 'select name, type || chr(13) type' -- chr(13) specific ASCII Carriage return
                ||' from   user_plsql_object_settings'
                ||' where name not like ''%$%'' and  rownum <= 10';      
    demo(sqlText);   
    sqlText := 'select 1 as one, 2 as two  from  dual ';                  
    demo(sqlText);

exception
    when others then
        dbms_output.put_line(sqlcode || ' - ' || sqlerrm);
end;

结果

dynamic sql: select 1 as one, 2 as two  from  dual where 1=0
                 ONE                 TWO


dynamic sql: select name, type || chr(13) type from   user_plsql_object_settings where name not like '%$%' and  rownum <= 10
                NAME                TYPE
     ADD_JOB_HISTORY          PROCEDURE
    AFT_INS_TEST_TRG            TRIGGER
    BEF_DEL_TEST_TRG            TRIGGER
    BEF_INS_TEST_TRG            TRIGGER
            BETWNSTR           FUNCTION
                BOOL           FUNCTION
    CACHED_FIBONACCI           FUNCTION
               DEBUG            PACKAGE
               DEBUG       PACKAGE BODY
          DEBUG_TEST          PROCEDURE


dynamic sql: select 1 as one, 2 as two  from  dual 
                 ONE                 TWO
                   1                   2

【讨论】:

疯了!所以有了 11g,我现在已经为每种类型的光标做好了准备! :) 我只是在想这些小东西,连同视图“user_identifier”(11g)、dbms_application_info 和(一点)努力,可以引导我完成一个很棒的代码检测过程。还是你已经写了? :-) 甜蜜!继续写那个程序! :)【参考方案2】:

AFAIK,没有办法直接从 PL/SQL 从REF CURSOR 获取元数据。奇怪的是,REF CURSOR 映射到 Java 的 ResultSet,可以通过调用 ResultSet.getMetaData 方法来查询元数据。

因此,您可以生成一个 Java 的存储过程来为您执行此操作。 Here你可以找到一个例子。

另一个选项是使用DBMS_SQL.TO_CURSOR_NUMBER(仅在 11g 中)将光标转换为数字光标,可以通过 DBMS_SQL 包询问元数据。

【讨论】:

我没有提到Java部分,但实际上你不能在空的REF CURSOR上调用ResultSet.getMetaData。那会抛出一个异常:-/这就是我问这个问题的最初原因。 +1 猜对了:) 事实证明,一个空的引用光标永远不会被实例化,这就是它不起作用的原因。我不知道,所以谢谢你的警告。似乎您忘记进行单元测试的那种错误:-) 没错!我添加了测试和 bang ! :)

以上是关于空 Oracle REF CURSOR 中的列名的主要内容,如果未能解决你的问题,请参考以下文章

让 .NET 了解返回的 REF_CURSOR 中的 Oracle 对象

oracle ref() oracle-00904 无效的列名

使用 JDBC 时,Oracle 的 REF CURSOR 在 Postgresql 中的等价物是啥?

如何在过程中使用返回 Oracle REF_CURSOR 的函数

CURSOR 和 REF CURSOR 作为 JDBC 数据类型

在存储过程中使用“out”ref_cursor 调用 Oracle 存储过程