2017-08-13 53 views
0

我有一個pl \ sql過程,需要使用dbms_sql包在curosr循環中查看記錄。使用光標記錄作爲數組

遊標查詢是動態的,所以你不知道列。

所以每次我想使用dbms_sql.define_columns或其他函數時,我都是通過all_tab_columns上的循環來獲取列名。

這是我的代碼:

procedure p is 

SOURCE_CURSOR  INTEGER; 
destination_cursor INTEGER; 
IGNORE    INTEGER; 
destination_cursor INTEGER; 
v_stmt clob := empty_clob(); 
V_COLS_LIST varchar2(4000); 
V_COLS_LIST2 varchar2(4000); 
V_UPDATE_DATE_COL_NAME varchar2(30) := 'UPDATE_DATE_COL'; 


begin 

    -- going over all the records. each record is a table 
    for CURR_TABLE in (select * from mng_tables) 
    loop 

     -- get the column list for the current table 
     SELECT LISTAGG(CLS.COLUMN_NAME, ',') WITHIN GROUP (ORDER BY COLUMN_ID) 
     INTO  V_COLS_LIST 
     FROM  ALL_TAB_COLUMNS CLS 
     WHERE CLS.TABLE_NAME = CURR_TABLE.HISTORY_TABLE_NAME 
     AND  CLS.OWNER = CURR_TABLE.HISTORY_TABLE_OWNER 
     AND  CLS.COLUMN_NAME <> V_UPDATE_DATE_COL_NAME; 

     -- prepare the select from current table 
     v_stmt := 'select ' || V_COLS_LIST || ', SYSDATE' || 
        ' from ' || CURR_TABLE.TABLE_OWNER || '.' || CURR_TABLE.TABLE_NAME; 

     -- prepare the dynamic sql   

     -- get cursor id 
     source_cursor := dbms_sql.open_cursor; 

     -- parse cursor with query 
     DBMS_SQL.PARSE(SOURCE_CURSOR,V_STMT, DBMS_SQL.NATIVE); 


     -- going over all the columns of current table and define matching columns 
     FOR rec in (SELECT * 
         FROM ALL_TAB_COLUMNS 
         WHERE CLS.TABLE_NAME = CURR_TABLE.HISTORY_TABLE_NAME 
         AND  CLS.OWNER = CURR_TABLE.HISTORY_TABLE_OWNER) 
     loop 

      DBMS_SQL.DEFINE_COLUMN(source_cursor, rec.column_id, rec.data_type); 

     end loop; 

     -- execute the select query 
     IGNORE := DBMS_SQL.EXECUTE(SOURCE_CURSOR); 

     -- define the destination cursor 
     destination_cursor := DBMS_SQL.OPEN_CURSOR; 

     select replace(V_COLS_LIST, ',' , ':,') 
     into V_COLS_LIST2 
     from dual; 

     -- parse the 
     DBMS_SQL.PARSE(destination_cursor, 
       'insert /*+ parallel(8) */ into ' || CURR_TABLE.HISTORY_TABLE_OWNER || '.' || CURR_TABLE.HISTORY_TABLE_NAME || 
       '(' || V_COLS_LIST || ',' || V_UPDATE_DATE_COL_NAME || ')' || 
       ' values (:' || V_COLS_LIST2 || ',sysdate)', 
       DBMS_SQL.NATIVE); 


     LOOP 

     -- if there is a row 
     IF DBMS_SQL.FETCH_ROWS(source_cursor)>0 THEN 

     FOR rec in (SELECT * 
         FROM ALL_TAB_COLUMNS 
         WHERE CLS.TABLE_NAME = CURR_TABLE.HISTORY_TABLE_NAME 
         AND  CLS.OWNER = CURR_TABLE.HISTORY_TABLE_OWNER) 
     loop 

     -- get column values of the row 
     DBMS_SQL.COLUMN_VALUE(source_cursor, rec.column_id, ???); 

     DBMS_SQL.BIND_VARIABLE(destination_cursor, ':' || rec.column_name, ???); 


     end loop; 

     ignore := DBMS_SQL.EXECUTE(destination_cursor); 

     ELSE 

    -- No more rows to copy: 
     EXIT; 
     END IF; 


     end loop; 


    end loop; 

end p; 

但是當我要綁定變量,我不能這樣做,監守我不能有動態值..

到底當我這樣做的程序:

DBMS_SQL.COLUMN_VALUE(source_cursor, rec.column_id, ???); 

DBMS_SQL.BIND_VARIABLE(destination_cursor, ':' || rec.column_name, ???); 

我只是想更換???像「my_rec [rec.column_name]」 或「my_rec [rec.column_id]」,並獲取該列中記錄的值。

有什麼想法?

謝謝。

回答

1

你正在使這個要複雜得多,效率也低得多。而不是生成一行一行地插入和選擇,並通過一個插入每行一個,您可以生成不每桌單個刀片的刀片,作爲選類型的語句:

create or replace procedure p is 
    v_stmt clob; 
    v_cols_list varchar2(4000); 
    v_update_date_col_name varchar2(30) := 'UPDATE_DATE_COL'; 
begin 
    -- going over all the records. each record is a table 
    for curr_table in (select * from mng_tables) 
    loop 
     -- get the column list for the current table 
     select '"' || listagg(cls.column_name, '","') 
      within group (order by column_id) || '"' 
     into  v_cols_list 
     from  all_tab_columns cls 
     where cls.table_name = curr_table.history_table_name 
     and  cls.owner = curr_table.history_table_owner 
     and  cls.column_name <> v_update_date_col_name; 

     -- generate an insert-select statement 
     v_stmt := 'insert into "' || curr_table.history_table_owner || '"' 
       || '."' || curr_table.history_table_name || '"' 
      || ' (' || v_cols_list || ', ' || v_update_date_col_name || ')' 
      || ' select ' || v_cols_list || ', sysdate' 
      || ' from "' || curr_table.table_owner || '"' 
       || '."' || curr_table.table_name || '"'; 

     -- just for debugging 
     dbms_output.put_line(v_stmt); 

     execute immediate v_stmt; 
    end loop; 
end p; 
/

我添加在所有的所有者,表和列名稱周圍用雙引號,以防萬一你有任何帶引號的標識符,但如果你確信你永遠不會那麼他們就不是真的有必要。

要回答雖然您的實際問題,簡單的蠻力方法是聲明一個字符串變量:

v_value varchar2(4000); 

,然後比column_value使用和bind_variable`電話:

DBMS_SQL.COLUMN_VALUE(source_cursor, rec.column_id, v_value); 
DBMS_SQL.BIND_VARIABLE(destination_cursor, rec.column_name, v_value); 

當您在兩個循環中沒有得到CLS別名時(這也不排除您的V_UPDATE_DATE_COL_NAME列),您的所發佈內容有許多問題,從CLS.TABLE_NAME等引用開始;你的DEFINE_COLUMN調用沒有指定數據長度,所以對於字符串列將無法正常工作;你的replace()將冒號放在逗號之前,而不是之後;並且你聲明destination_cursor兩次。

但是這個作品,如果我理解你的架構:

create or replace procedure p is 

SOURCE_CURSOR  INTEGER; 
destination_cursor INTEGER; 
IGNORE    INTEGER; 
v_stmt clob := empty_clob(); 
V_COLS_LIST varchar2(4000); 
V_COLS_LIST2 varchar2(4000); 
V_UPDATE_DATE_COL_NAME varchar2(30) := 'UPDATE_DATE_COL'; 
v_value varchar2(4000); 

begin 

    -- going over all the records. each record is a table 
    for CURR_TABLE in (select * from mng_tables) 
    loop 

     -- get the column list for the current table 
     SELECT LISTAGG(CLS.COLUMN_NAME, ',') WITHIN GROUP (ORDER BY COLUMN_ID) 
     INTO  V_COLS_LIST 
     FROM  ALL_TAB_COLUMNS CLS 
     WHERE CLS.TABLE_NAME = CURR_TABLE.HISTORY_TABLE_NAME 
     AND  CLS.OWNER = CURR_TABLE.HISTORY_TABLE_OWNER 
     AND  CLS.COLUMN_NAME <> V_UPDATE_DATE_COL_NAME; 

     -- prepare the select from current table 
     v_stmt := 'select ' || V_COLS_LIST || ', SYSDATE' || 
        ' from ' || CURR_TABLE.TABLE_OWNER || '.' || CURR_TABLE.TABLE_NAME; 

     -- prepare the dynamic sql   

     -- get cursor id 
     source_cursor := dbms_sql.open_cursor; 

     -- parse cursor with query 
     DBMS_SQL.PARSE(SOURCE_CURSOR,V_STMT, DBMS_SQL.NATIVE); 


     -- going over all the columns of current table and define matching columns 
     FOR rec in (SELECT * 
         FROM ALL_TAB_COLUMNS CLS 
         WHERE CLS.TABLE_NAME = CURR_TABLE.HISTORY_TABLE_NAME 
         AND  CLS.OWNER = CURR_TABLE.HISTORY_TABLE_OWNER 
         AND  CLS.COLUMN_NAME <> V_UPDATE_DATE_COL_NAME) 
     loop 

      DBMS_SQL.DEFINE_COLUMN(source_cursor, rec.column_id, rec.data_type, rec.data_length); 

     end loop; 

     -- execute the select query 
     IGNORE := DBMS_SQL.EXECUTE(SOURCE_CURSOR); 

     -- define the destination cursor 
     destination_cursor := DBMS_SQL.OPEN_CURSOR; 

     select replace(V_COLS_LIST, ',' , ',:') 
     into V_COLS_LIST2 
     from dual; 

     -- parse the 
     DBMS_SQL.PARSE(destination_cursor, 
       'insert /*+ parallel(8) */ into ' || CURR_TABLE.HISTORY_TABLE_OWNER || '.' || CURR_TABLE.HISTORY_TABLE_NAME || 
       '(' || V_COLS_LIST || ',' || V_UPDATE_DATE_COL_NAME || ')' || 
       ' values (:' || V_COLS_LIST2 || ',sysdate)', 
       DBMS_SQL.NATIVE); 


     LOOP 

     -- if there is a row 
     IF DBMS_SQL.FETCH_ROWS(source_cursor)>0 THEN 

     FOR rec in (SELECT * 
         FROM ALL_TAB_COLUMNS CLS 
         WHERE CLS.TABLE_NAME = CURR_TABLE.HISTORY_TABLE_NAME 
         AND  CLS.OWNER = CURR_TABLE.HISTORY_TABLE_OWNER 
         AND  CLS.COLUMN_NAME <> V_UPDATE_DATE_COL_NAME) 
     loop 

     -- get column values of the row 
     DBMS_SQL.COLUMN_VALUE(source_cursor, rec.column_id, v_value); 

     DBMS_SQL.BIND_VARIABLE(destination_cursor, rec.column_name, v_value); 


     end loop; 

     ignore := DBMS_SQL.EXECUTE(destination_cursor); 

     dbms_sql.close_cursor(destination_cursor); 
     ELSE 

    -- No more rows to copy: 
     EXIT; 
     END IF; 


     end loop; 


    end loop; 

end p; 
/

倒不如讓每個可能的數據類型的變量,並使用一個case語句來調用column_value和bind_variable`與正確因爲你不依賴於字符串間的隱式轉換(尤其是日期問題 - 根據會話NLS設置可能會失去精度)。

+0

我有我的理由不使用inser-select,即使它實際上是微不足道的方法。我修正了發佈的問題,這就是當你不把我原來的代碼時會發生什麼。你的代碼幫助了我!非常感謝! – user2671057