空 Oracle REF CURSOR 中的列名

发布于 2024-11-18 04:00:55 字数 405 浏览 4 评论 0原文

REF CURSOR 中找出一行中每一列的名称/值对:

在 PL/SQL 中,我可以使用类似这样的技巧, com/questions/6265160/to-char-of-an-oracle-pl-sql-table-type">Oracle PL/SQL TABLE 类型的 TO_CHAR

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

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

:空的REF CURSOR甚至有列名称/类型信息?

In PL/SQL, I can use a trick like this one here to find out name/value pairs for every column in a row from a REF CURSOR:

TO_CHAR of an Oracle PL/SQL TABLE type

That's an awesome trick. But it doesn't work when the REF CURSOR is empty, such as this one here (that's just an example. The real cursor doesn't select from DUAL):

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

Does an empty REF CURSOR even have column name/type information?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

满地尘埃落定 2024-11-25 04:00:55

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

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

Yes, I've tried that solution without rows, and you're right.
From my limited point of view, I think here we need two different methods to retrieve columns' names and values.

1) Dbms_sql package to retrieve the columns' names.

2) The tbone method to retrieve the data.


Procedure

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;

Test

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;

Result

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
病毒体 2024-11-25 04:00:55

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

因此,您可以生成 Java 的存储过程来为您执行此操作。 在这里您可以找到一个示例。

另一种选择是使用 DBMS_SQL.TO_CURSOR_NUMBER(仅在 11g 中)将游标转换为数字游标,可以使用 DBMS_SQL 包。

AFAIK, there is no way of getting metadata from a REF CURSOR directly from PL/SQL. Curiously, a REF CURSOR maps to a Java's ResultSet, which can be queried for metadata calling its ResultSet.getMetaData method.

So you could generate a Java's stored procedure to do that for you. Here you can find an example.

Another option is convert the cursor to a numeric cursor using DBMS_SQL.TO_CURSOR_NUMBER (only in 11g), which can be asked for metadata with the DBMS_SQL package.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文