Oracle数据库迁移ZHS16GBK

背景:两个数据库服务器,分别对应两个数据库,每一个数据库对应一个实例,每一个实例存在两个用户分别管理各自的数据,其中一个数据库采用ZHS16GBK编码,另外一个数据库采用AL32UTF8编码,现将数据整合至一个采用AL32UTF8编码的数据库中完成数据的迁移。html

数据库编码类型,避免错误sql

--SQL--
SELECT * FROM V$NLS_PARAMETERS WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');

完成编码确认之后就进行某度,提示各类方法尝试,例如修改字符编码,直接导库,设置编码后导库,结果都是有问题,并且问题仍是不小,着实被坑了一把。因此能放弃某度就放弃他吧,目前最直观的结果就是带给你效率跟不上。数据库

直接修改编码,失败,不过也提供代码参考,能够解决部分非中文字符问题,惨案以下:bash

--SQL--
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
ALTER SYSTEM ENABLE RESTRICTED SESSION;
ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0;
ALTER SYSTEM SET AQ_TM_PROCESSES=0;
ALTER DATABASE OPEN;
--进行字符编码的切换,理论切换成功了会自动进行编码转换,可是会提示非超集问题,瞬间尴尬了,都不知道ZHS16GBK和AL32UTF8到底谁是谁的超集
--ALTER DATABASE CHARACTER SET AL32UTF8;
--因为上述的操做失败,直接设置字符集编码,该方法可以修改字符集,可是相应的内容并未真的进行编码转换,甚至致使乱码和数据查询失败,因此仍是不起做用
ALTER DATABASE CHARACTER SET INTERNAL_USE AL32UTF8;
SHUTDOWN IMMEDIATE;
STARTUP;

修改编码后导库服务器

一、这里涉及到多用户问题,单用户分别导出,而后导入到新的库会提示DBMS_JOB之类的警示信息,因此须要将多用户数据同时导出到一个dmp文件中,这样才能避免这个问题;性能

二、可是因为这种方法的设置编码后执行,依然没法解决跨编码导库问题,提示对应的字符长度不匹配,所以仍然失败。编码

惨案以下:spa

::bat
set NLS_LANG=SIMPLIFIED CHINESE_CHINA.AL32UTF8
::导出用户user1,user2的全部相关表至db.dmp文件中,操做日志为db.log
exp user/pwd@db log=db.log file=db.dmp owner=user1,user2

上述的方法执行了不少遍,而后问题没法解决,主要数据库数据量仍是有一点的中间耗时比较多,最后找到部分新的解决思路,将数据库全部的varchar2的字段的长度调整1.5倍,连接以下:操作系统

http://blog.itpub.net/28602568/viewspace-1261407/.net

http://www.javashuo.com/article/p-ukusruso-dr.html

上述二者很是明确,也提供了很好的思路,可是因为其sql语句不懂这么执行,主要是我的数据库水平有限,操做不成功因此只能重写本身调整

--SQL--
SELECT
'ALTER TABLE ' || OWNER || '.' || TABLE_NAME || ' MODIFY( ' || COLUMN_NAME || ' VARCHAR2 (' || 
  (case when DATA_LENGTH * 1.5 > 4000 then '4000' else to_char(Ceil(DATA_LENGTH *1.5)) end)
   || '));'
  FROM DBA_TAB_COLUMNS UTC
 WHERE UTC.OWNER = UPPER('user')
   AND UTC.DATA_TYPE = 'VARCHAR2'
   AND UTC.TABLE_NAME NOT IN
       (SELECT TABLE_NAME
          FROM DBA_EXTERNAL_TABLES E
         WHERE E.OWNER = UPPER('user')) --筛选掉临时表
   AND UTC.TABLE_NAME NOT LIKE 'BIN%';
 --直接用本身的用户名替换到user
 --一、执行上上述sql语句
 --二、将执行的结果拷贝值新的sql,而后批量执行,忽略视图错误,这样就将全部varchar2的字段长度调整成功

这样就能够进行顺利的执行转换操做了,如下是相对完整的sql与bat:

关于多用户多文件操做,直接在对应文件中添加相应的sql语句便可,因为部分语法的差别,须要自行检查语句对应的结束符;注释-- /**/等等,这样的细节会致使意想不到的状况出现。

--SQL--
--方案一匹配exp/imp

--连接数据库
--conn user/pwd@dbinstance as sysdba;
--删除用户与用户数据,若是容许的话就删除,若是不容许只能在exp或imp语句中增长参数控制
drop user user cascade;
--从新建立用户,避免使用exp自动生成分配的帐号用户,由于会致使其余问题,好比说用户被锁定
CREATE USER user IDENTIFIED BY "pwd"
/
--赋予DBA权限,根据设置的用户状况自行定义
GRANT DBA TO user
/
commit;
--若是有须要能够删除表空间和表空间文件,避免经过操做系统的方式直接删除表空间文件,否者会引发其余问题,例如数据库没法打开
drop tablespace tb_name including contents and datafiles;
--若是表空间被删除或表空间不存在则须要建立表空间,
--一、须要设置初始size大一些,避免表空间不足致使的异常;
--二、设置autoextend自动表空间增加,避免数据量大时致使的表空间膨胀不足;
--三、next,设置每次的扩张阈值尽可能设置大写,若是太小也会致使导入数据产生异常
--四、maxsize,根据实际状况调整,若是不涉及所谓性能问题直接设置为无限制,这样避免数据膨胀致使的扩容异常
create tablespace tb_name
  logging   
  datafile 'C:\Program Files\Oracle\tablespace\tb_name.dbf'     
  size 1024m 
  autoextend on
  NEXT 200M MAXSIZE UNLIMITED
  extent management local;   
--退出SQL语句
exit;

方案二,要求在对应服务器机器与客户机执行,禁止远程操做

--SQL--
--方案二匹配expdp/impdp
--连接数据库
--conn user/pwd@dbinstance as sysdba;
--删除用户与用户数据,若是容许的话就删除,若是不容许只能在exp或imp语句中增长参数控制
drop user user cascade;
--从新建立用户,避免使用exp自动生成分配的帐号用户,由于会致使其余问题,好比说用户被锁定
CREATE USER user IDENTIFIED BY "pwd"
/
--赋予DBA权限,根据设置的用户状况自行定义
GRANT DBA TO user
/
commit;
--建立数据库路径,必须设置或使用以前已经存在的路径
--查询已经存在的路径
--select * from dba_directories;
create directory dpdata as 'C:\Program Files\Oracle\tablespace';
--给对应的数据库路径设置权限
grant read,write on directory dpdata to user
/
GRANT CONNECT TO user WITH ADMIN OPTION
/
GRANT RESOURCE TO user
/
-- 须要在数据导入端单独执行
--grant imp_full_database to user with admin option
/
--因为expdp要求在服务端执行,不支持远程操做,故须要在导出端单独执行该语句
--grant exp_full_database to user with admin option
/
commit;

--若是有须要能够删除表空间和表空间文件,避免经过操做系统的方式直接删除表空间文件,否者会引发其余问题,例如数据库没法打开
drop tablespace tb_name including contents and datafiles;
--若是表空间被删除或表空间不存在则须要建立表空间,
--一、须要设置初始size大一些,避免表空间不足致使的异常;
--二、设置autoextend自动表空间增加,避免数据量大时致使的表空间膨胀不足;
--三、next,设置每次的扩张阈值尽可能设置大写,若是太小也会致使导入数据产生异常
--四、maxsize,根据实际状况调整,若是不涉及所谓性能问题直接设置为无限制,这样避免数据膨胀致使的扩容异常
create tablespace tb_name
  logging   
  datafile 'C:\Program Files\Oracle\tablespace\tb_name.dbf'     
  size 1024m 
  autoextend on
  NEXT 200M MAXSIZE UNLIMITED
  extent management local;   
--退出SQL语句
exit;

如下关于执行的脚本

::bat
::方案一,匹配exp/imp在exp imp语句结束禁止加任何结束符
@echo off
::首选确认有没有设置%ORACLE_HOME%环境变量,而后确认是否在path环境变量中追加%ORACLE_HOME%/bin
::start /max ""  "%ORACLE_HOME%/bin"
::设置当前目录为导出导入的工做路径
set cur_dir=%cd%
:exp
::导出库
exp user/user@dbinstance log=%cur_dir%\db.log file=%cur_dir%\db.dmp owner=user1,user2
::导出完成后打开相应的目录自行找到对应的log检查是否执行正确
start /max ""  "%cur_dir%"
:imp
:: 确认表空间文件是否存在,若是不存在则建立,完成后打开对应表空间目录
set tablespace_path="C:\Program Files\Oracle\tablespace"
md %tablespace_path%
start /max ""  "%tablespace_path%"
sqlplus orcl/orcl@orcl as sysdba @%cur_dir%/create_user.sql

imp user/pwd log=%cur_dir%\impdb.log file=.%cur_dir%\db.dmp ignore=y fromuser=user_1 touser1 buffer=819200
imp user/pwd log=%cur_dir%\impdb.log file=%cur_dir%\db.dmp ignore=y fromuser=user_2 touser=user2 buffer=819200
::pause

方案二bat

::bat
::方案二,匹配expdp/impdp在expdp impdp语句结束禁止加任何结束符
@echo off
::首选确认有没有设置%ORACLE_HOME%环境变量,而后确认是否在path环境变量中追加%ORACLE_HOME%/bin
::start /max ""  "%ORACLE_HOME%/bin"
:pre
set cur_dir=%cd%
:: 确认表空间文件是否存在,若是不存在则建立,完成后打开对应表空间目录
set tablespace_path="C:\Program Files\Oracle\tablespace"
md %tablespace_path%
start /max ""  "%tablespace_path%"
::执行sql语句完成对应用户表空间表路径等建立
sqlplus user/pwd@dbinstance as sysdba @%cur_dir%/expdp_sql.sql
:exp
::须要在服务端导出库,不支持远程操做
expdp user/pwd@dbinstance schemas=user1,user2 dumpfile=expdp_db.dmp  logfile=expdp_db.log DIRECTORY=dpdata content=all
:imp
impdp user/pwd DIRECTORY=dpdata logfile=impdb_db.log dumpfile=expdp_db.dmp REMAP_SCHEMA=userfrom:userto
::pause

如此操做便可完成所有的导入导出操做,期间还会出现DBMS_JOB等异常,能够忽略,也能够自行找办法解决,后续根据实际状况进一步调整,若是导入导出过程当中出现了异常状况,修改设置exp对应参数设置具体能够参考官方的文档