一次Oracle数据迁移

目标数据库:Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 

源数据库  : Oracle Database 11g Enterprise Edition Release 11.2.0.1.0

1.首先想到的是用expdp,impdp。

 通过交流发现无法得到源数据库的操作系统密码,这样一来expdp,impdp就不好使了。

2.其次想到的是用plsql developer 来导出数据,但是导出的时候报错了。

放弃此种方法。

3.由于数据量不是很大,考虑使用数据库链的方式来完成。

 a.在目标数据库上创建数据库链,链接到目标数据库。

create public database link
  to_168_bi22 connect to "bi41" identified by "bi41" using 
'(DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST =172.21.1.68)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = orcl)
    )
  )';

 b.得到用户对象的定义语句 dbms_metadata.get_ddl('TABLE','ACT_GE_BYTEARRAY')

 在此纠结了一下,是直接用过程脚本来创建表,还是先生成建表语句,之后再循环灌数据。

c.选择先创建用户对象,之后再插入数据。

 这个选择给后续的数据插入带来了一些麻烦,就是索引和约束,特别是外键约束。

在脚本中加入了禁用约束的语句

FOR i IN (SELECT table_name, constraint_name --disable first the foreign key
                FROM user_constraints
               WHERE constraint_type = 'R'
                 AND status = 'ENABLED') LOOP
      EXECUTE IMMEDIATE 'alter table "' || i.table_name || '" disable constraint ' ||
                        i.constraint_name;
    END LOOP i;
    FOR i IN (SELECT table_name, constraint_name -- then disable all constraints
                FROM user_constraints
               WHERE status = 'ENABLED') LOOP
      EXECUTE IMMEDIATE 'alter table "' || i.table_name || '" disable constraint ' ||
                        i.constraint_name;
    END LOOP i;

感觉OK的时候,又报错了。

 ERROR:  ORA-22992: cannot use LOB locators selected from remote tables 

原来表中有些是LOB字段,无法通过数据链访问直接访问。

最后想到用Oracle的全局临时表的方式将数据抽取过来。

这里也有一个小插曲,就是有50多个表都是有lob字段的。

想写一个循环利用动态SQL来创建global temporary table,并且每次创建之前删除掉改全局临时表。

v_sql := '
    create global temporary table table1  
        ON COMMIT PRESERVE ROWS as
    select * from ' || v_table_name;
    execute immediate v_sql;
  
    v_sql := 'insert into table1 ' || '
    select * from  ' || v_table_name || '@to_168_bi';
    execute immediate v_sql;
v_sql := 'insert into ' || v_table_name || ' select * from table1'; execute immediate v_sql;

commit;

这样创建的临时表默认是 COMMIT delete ROWS 的。

发现数据没有抽取过来,初步怀疑是动态执行SQL的时候自动提交了,这里有些没有想清楚。

之后创建全局临时表的时候加上了ON COMMIT PRESERVE ROWS。

数据是抽过来了,但是drop的时候会报错:ORA-14452: 试图创建, 变更或删除正在使用的临时表中的索引

declare
  v_table_name varchar2(32);
  v_sql        varchar2(2000);
  v_cnt number;
  
  cursor cur is
    select t.TABLE_NAME
      from user_tables t
     where t.TABLE_NAME
           in (select st.table_name
                 from user_tab_columns st
                where st.DATA_TYPE in ('CLOB', 'BLOB'))
     order by t.TABLE_NAME;

begin

  for i in cur loop
    v_table_name := i.table_name;
    v_sql        := 'truncate table ' || v_table_name;
    execute immediate v_sql;
    
    select count(1) into v_cnt from user_tables t 
    where t.TABLE_NAME=upper('table1');
    
    if v_cnt >0 then
       execute immediate 'drop table table1';
    end if; 
    
    v_sql := 'create global temporary table table1    
    ON COMMIT PRESERVE ROWS 
    as' || '
    select * from  ' || v_table_name ||'@to_168_bi';
    execute immediate v_sql;
  
    v_sql := 'insert into  ' || v_table_name || '
    select * from table1';
    execute immediate v_sql;
    commit;
  end loop;

exception
  when others then
    dbms_output.put_line(v_table_name || ':' || sqlcode || ':' || sqlerrm);
  
end get_data_from168;
View Code

之后测试了一下,

SQL> create global temporary table table1 as select * from employees;

Table created.

SQL> drop table table1;

Table dropped.

SQL> create global temporary table table1 on commit preserve rows as select * from employees;

Table created.

SQL> drop table table1;
drop table table1
           *
ERROR at line 1:
ORA-14452: attempt to create, alter or drop an index on temporary table already
in use

实验证明创建glob temporary table 的时候如果添加了on commit preserve rows在session没有退出的情况下是没发drop的。

之后这样写在SQLplus 中执行却没什么问题,plsql developer 的test procedure的方式是多session?

declare
  v_table_name varchar2(32);
  v_sql        varchar2(2000);
  v_cnt number;
  
  cursor cur is
    select t.TABLE_NAME
      from user_tables t
     where t.TABLE_NAME
           in (select st.table_name
                 from user_tab_columns st
                where st.DATA_TYPE in ('CLOB', 'BLOB'))
     order by t.TABLE_NAME;

begin

  for i in cur loop
    v_table_name := i.table_name;
    v_sql        := 'truncate table ' || v_table_name;
    execute immediate v_sql;
    
    select count(1) into v_cnt from user_tables t 
    where t.TABLE_NAME=upper('table1');
    
    if v_cnt >0 then
       execute immediate 'drop table table1';
    end if; 
    
   /* v_sql := 'create global temporary table table1    
    ON COMMIT PRESERVE ROWS 
    as' || '
    select * from  ' || v_table_name ||'@to_168_bi';
    execute immediate v_sql;*/
        
        v_sql := 'create global temporary table table1
    as' || '
    select * from  ' || v_table_name;
    execute immediate v_sql;
        
        execute immediate ('insert into table1 select * from  '||v_table_name||'@to_168_bi');
  
    v_sql := 'insert into  ' || v_table_name || '
    select * from table1';
    execute immediate v_sql;
    commit;
  end loop;

exception
  when others then
    dbms_output.put_line(v_table_name || ':' || sqlcode || ':' || sqlerrm);
  
end get_data_from168;
View Code

后来想想,如果用Kettle的话会更方便,结果用Kettle测试了一下,Kettle对于lob字段处理的也非常好。

之后Kettle会自动生成每个表对应的转换。

测试运行也没什么问题,非常方便。

原文地址:https://www.cnblogs.com/Alex-Zeng/p/3988504.html