2017-08-13 107 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