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者請教。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM