• ORACLE中使用DBMS_SQL获取动态SQL执行结果中的列名和值


    1.获取动态SQL中的列名及类型

    DECLARE
      l_curid   INTEGER;
      l_cnt     NUMBER;
      l_desctab dbms_sql.desc_tab;
      l_sqltext VARCHAR2(2000);
    BEGIN
      l_sqltext := 'select *
    from dba_objects  where rownum<= 10'; --可以是任意有效的查询sql文本 
      l_curid   := dbms_sql.open_cursor();
      dbms_sql.parse(l_curid, l_sqltext, dbms_sql.native);
      dbms_sql.describe_columns(l_curid, l_cnt, l_desctab);
      FOR i IN 1 .. l_desctab.count LOOP
        dbms_output.put_line(rpad(l_desctab(i).col_name, 30)||rpad(l_desctab(i).col_type, 3));
      END LOOP;
      dbms_sql.close_cursor(l_curid);
    END;

    查询结果

    OWNER                         1  
    OBJECT_NAME                   1  
    SUBOBJECT_NAME                1  
    OBJECT_ID                     2  
    DATA_OBJECT_ID                2  
    OBJECT_TYPE                   1  
    CREATED                       12 
    LAST_DDL_TIME                 12 
    TIMESTAMP                     1  
    STATUS                        1  
    TEMPORARY                     1  
    GENERATED                     1  
    SECONDARY                     1  
    NAMESPACE                     2  
    EDITION_NAME                  1  
    SHARING                       1  
    EDITIONABLE                   1  
    ORACLE_MAINTAINED             1  
    col_type 1:VARCAHR2,2:NUMBER,12:DATE

    2.使用USING方式绑定动态SQL,获取列名及对应的值

    -- Created on 2017/10/11 by ADMINISTRATOR 
    DECLARE
      TYPE typecursor IS REF CURSOR;
      cursrc    typecursor;
      curid     NUMBER;
      desctab   dbms_sql.desc_tab;
      colcnt    NUMBER;
      vname     VARCHAR2(50);
      vnum      NUMBER;
      vdate     DATE;
      rownumber NUMBER := 5;
    
      sqlstmt VARCHAR2(2000);
    BEGIN
      sqlstmt := 'SELECT * FROM fnd_user WHERE rownum < :rownumber';
      -- 打开光标
      OPEN cursrc FOR sqlstmt
      USING rownumber;
        
      -- 从本地动态SQL转换为DBMS_SQL
      curid := dbms_sql.to_cursor_number(cursrc);
    --获取游标里面的数据列项数和每个数据列的属性,比如列名,类型,长度等 dbms_sql.describe_columns(curid, colcnt, desctab);
    -- 定义列 FOR i IN 1 .. colcnt LOOP
    --此处是定义游标中列的读取类型,可以定义为字符,数字和日期类型,
    IF desctab(i).col_type = 2 THEN dbms_sql.define_column(curid, i, vnum); ELSIF desctab(i).col_type = 12 THEN dbms_sql.define_column(curid, i, vdate); ELSE dbms_sql.define_column(curid, i, vname, 50); END IF; END LOOP; -- DBMS_SQL包获取行
    --从游标中把数据检索到缓存区(BUFFER)中,缓冲区 的值只能被函数COULUMN_VALUE()所读取
    WHILE dbms_sql.fetch_rows(curid) > 0 LOOP
       --函数column_value()把缓冲区的列的值读入相应变量中。
    FOR i IN 1 .. colcnt LOOP IF (desctab(i).col_type = 1) THEN dbms_sql.column_value(curid, i, vname); dbms_output.put_line(desctab(i).col_name || ' ' || vname || ', '); ELSIF (desctab(i).col_type = 2) THEN dbms_sql.column_value(curid, i, vnum); dbms_output.put_line(desctab(i).col_name || ' ' || vnum || ', '); ELSIF (desctab(i).col_type = 12) THEN dbms_sql.column_value(curid, i, vdate); dbms_output.put_line(desctab(i).col_name || ' ' || to_char(vdate, 'YYYY-MM-DD HH24:MI:SS') || ', '); END IF; END LOOP; END LOOP; dbms_sql.close_cursor(curid); END;

    3.使用DBMS_SQL.BIND_VARIABLE方式传递参数,获取列名及对应的值

    DECLARE
      v_cursor NUMBER;
      v_stat   NUMBER;
      v_row    NUMBER;
      v_id     NUMBER;
      v_no     VARCHAR(100);
      v_date   DATE;
      v_sql    VARCHAR(200);
      s_id     NUMBER;
      s_date   DATE;
    BEGIN
      s_id     := 1131;
      s_date   := SYSDATE;
      v_sql    := 'SELECT fu.user_id, fu.user_name, fu.CREATION_DATE FROM fnd_user fu where fu.user_id = :userId';
      v_cursor := dbms_sql.open_cursor; --打开游标;
      dbms_sql.parse(v_cursor, v_sql, dbms_sql.native); --解析动态SQL语句;
      dbms_sql.bind_variable(v_cursor, ':userId', s_id); --绑定输入参数;
    
      dbms_sql.define_column(v_cursor, 1, v_id); --定义列
      dbms_sql.define_column(v_cursor, 2, v_no, 100);
      dbms_sql.define_column(v_cursor, 3, v_date);
      v_stat := dbms_sql.execute(v_cursor); --执行动态SQL语句。
      LOOP
        EXIT WHEN dbms_sql.fetch_rows(v_cursor) <= 0; --fetch_rows在结果集中移动游标,如果未抵达末尾,返回1。        
        dbms_sql.column_value(v_cursor, 1, v_id); --将当前行的查询结果写入上面定义的列中。
        dbms_sql.column_value(v_cursor, 2, v_no);
        dbms_sql.column_value(v_cursor, 3, v_date);
        dbms_output.put_line(v_id || ';' || v_no || ';' || v_date);
      END LOOP;
      dbms_sql.close_cursor(v_cursor); --关闭游标。
    END;

    参考:

    PLSQL Language Referenc-PL/SQL动态SQL-DBMS_SQL包-DBMS_SQL.TO_CURSOR_NUMBER()函数 

    使用Oracle的DBMS_SQL包执行动态SQL语句

    Oracle之DBMS_SQL包用法详解

    PLSQL中怎样获取未知结构的动态游标(REF CURSOR)的字段


    最后一份来自百度文库的不好复制,现整理如下

      对于使用过 ORACLE PLSQL 中的动态游标的人来说,我相信有不少人都会有这样的想法:如果对于任意一个给定的未知结构的游标(REF CURSOR),我们都能够在PLSQL中获取它的所有字段的名称,那该多好啊!不知道你是否有这样的想法,反正我早就有这样的想法了,也百度了多次,但结果不尽人意。曾经一度以为,这是不可能的。但是PLSQL Developer中的test 窗口中,可以打开任意游标并得到字段名及值。很显然,还是有办法得到未知结构的动态游标的字段名的,只是我不知道方法而已。
      今天早上心血来潮,又想到这个事情,于是google了一下(用英文查询:how to get column names from oracle cursor),发现还真有办法获取未知结构的动态游标!看来在这方面百度还是太弱啊!技术问题还是得问google。
      整理之后,结论如下:

    1、如果给的是一个查询SQL文本,那么事情很容易(对于9i及以上版本),只要使用dbms_sql.open_cursor打开游标,再使用dbms_sql.describe_columns即可得到游标的所有字段名称及类型等数据,存储在一个集合类型变量中(具体请看dbms_sql.desc_tab)。请参考如下PLSQL代码:

    DECLARE
      l_curid   INTEGER;
      l_cnt     NUMBER;
      l_desctab dbms_sql.desc_tab;
      l_sqltext VARCHAR2(2000);
    BEGIN
      l_sqltext := 'select *
    from dba_objects  where rownum<= 10'; --可以是任意有效的查询sql文本 
      l_curid   := dbms_sql.open_cursor();
      dbms_sql.parse(l_curid, l_sqltext, dbms_sql.native);
      dbms_sql.describe_columns(l_curid, l_cnt, l_desctab);
      FOR i IN 1 .. l_desctab.count LOOP
        dbms_output.put_line(rpad(l_desctab(i).col_name, 30)||rpad(l_desctab(i).col_type, 3));
      END LOOP;
      dbms_sql.close_cursor(l_curid);
    END;

      注意,必须使用 DBMS_SQL.OPEN_CURSOR 打开游标,否则,就不是这种处理方法了。
      2、如果给的是一个REF CURSOR类型变量,而不知道SQL文本,该怎么办呢?这里分两种情况:
      1) 如果数据库版本是11g及以上,同样很容易:使用dbms_sql.to_cursor_number(cursor) 得到游标的ID,再使用dbms_sql.describe_columns即可得到游标字段名称。参考如
    下代码:

    DECLARE
      TYPE ref_cursor IS REF CURSOR;
      l_cursor   ref_cursor;
      l_curid    NUMBER;
      l_col_cnt  NUMBER;
      l_desc_tab dbms_sql.desc_tab;
    BEGIN
      OPEN l_cursor FOR 'select owner,object_type,object_name from dba_objects where rownum<= 10';
      l_curid := dbms_sql.to_cursor_number(l_cursor);
      dbms_sql.describe_columns(l_curid, l_col_cnt, l_desc_tab);
      FOR i IN 1 .. l_col_cnt LOOP
        dbms_output.put_line(l_desc_tab(i).col_name);
      END LOOP;
      dbms_sql.close_cursor(l_curid);
    END;

      2) 如果数据库版本低于11g,则PLSQL中仅有如下方法可以获取字段名称及字段值: 

    DECLARE
      l_cursor SYS_REFCURSOR;
      i        NUMBER := 0;
      CURSOR get_columns IS
        SELECT t2.column_value.getrootelement() NAME,
               extractvalue(t2.column_value, 'node()') VALUE
          FROM (SELECT * FROM TABLE(xmlsequence(l_cursor))) t1,
               TABLE(xmlsequence(extract(t1.column_value, '/ROW/node()'))) t2;
    BEGIN
      OPEN l_cursor FOR 'select owner,object_type,object_name from dba_objects where rownum<= 10';
      FOR rec_ IN get_columns LOOP
        i := i + 1;
        dbms_output.put_line(rpad(i,2) || ':' || rpad(rec_.name,15) || ': ' || lpad(rec_.value,15));
      END LOOP;
      CLOSE l_cursor;
    END;

      用这种方法,可以得到动态游标的所有数据,包括字段名称和字段值。但这种方法会遍历游标,即游标已经走到底了,不能再次使用了。而使用dbms_sql.describe_columns不会对游标的光标位置产生任何影响。两者优劣一目了然。
      对于JAVA、C等外部编程语言而言,要从游标中获取字段名称是比较容易的。可以用JAVA语言写一个获取游标字段名称的存储过程,并编译至数据库中,且做成PLSQL接口,这样就可以在PLSQL中调用了。有意者可以自己去研究下,或者向精通JAVA者请教。

  • 相关阅读:
    .net core
    asp.net core之abp框架
    C#
    c#
    C#
    C#
    C#
    技术术语
    mysql
    006.内测.情景之迷你财务记账
  • 原文地址:https://www.cnblogs.com/huanghongbo/p/7650499.html
Copyright © 2020-2023  润新知