背景:两个数据库服务器,分别对应两个数据库,每一个数据库对应一个实例,每一个实例存在两个用户分别管理各自的数据,其中一个数据库采用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对应参数设置具体能够参考官方的文档