背景:两个数据库服务器,分别对应两个数据库,每个数据库对应一个实例,每个实例存在两个用户分别管理各自的数据,其中一个数据库采用ZHS16GBK编码,另一个数据库采用AL32UTF8编码,现将数据整合至一个采用AL32UTF8编码的数据库中完成数据的迁移。
--sql-- SELECT * FROM V$NLS_PARAMETERS WHERE PARAMETER IN ('NLS_CHARACTERSET','NLS_NCHAR_CHARACTERSET');
完成编码确认以后就进行某度,提示各种方法尝试,例如修改字符编码,直接导库,设置编码后导库,结果都是有问题,而且问题还是不小,着实被坑了一把。所以能放弃某度就放弃他吧,目前最直观的结果就是带给你效率跟不上。
直接修改编码,失败,不过也提供代码参考,可以解决部分非中文字符问题,惨案如下:
--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;
修改编码后导库
1、这里涉及到多用户问题,单用户分别导出,然后导入到新的库会提示DBMS_JOB之类的警示信息,所以需要将多用户数据同时导出到一个dmp文件中,这样才能避免这个问题;
2、但是由于这种方法的设置编码后执行,依然无法解决跨编码导库问题,提示对应的字符长度不匹配,因此仍然失败。
惨案如下:
::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/
https://www.cnblogs.com/huangen88/p/4664759.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 --1、执行上上述sql语句 --2、将执行的结果拷贝值新的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; --如果表空间被删除或表空间不存在则需要创建表空间, --1、需要设置初始size大一些,避免表空间不足导致的异常; --2、设置autoextend自动表空间增长,避免数据量大时导致的表空间膨胀不足; --3、next,设置每次的扩张阈值尽量设置大写,如果过小也会导致导入数据产生异常 --4、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; --如果表空间被删除或表空间不存在则需要创建表空间, --1、需要设置初始size大一些,避免表空间不足导致的异常; --2、设置autoextend自动表空间增长,避免数据量大时导致的表空间膨胀不足; --3、next,设置每次的扩张阈值尽量设置大写,如果过小也会导致导入数据产生异常 --4、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对应参数设置具体可以参考官方的文档