目标数据库: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;
之后测试了一下,
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;
后来想想,如果用Kettle的话会更方便,结果用Kettle测试了一下,Kettle对于lob字段处理的也非常好。
之后Kettle会自动生成每个表对应的转换。
测试运行也没什么问题,非常方便。