EXP和IMP以其简单易用,跨平台,向下兼容等特性是我们做数据迁移最常使用的工具,同时也可以做为oracle数据库逻辑备份的工具。EXP和IMP是oracle提供的一套工具,不需要额外安装。EXP和IMP的使用比较简单,具体的语法可以查看帮助文档:exp/imp -help,本文不在语法上做过多的纠缠,主要是介绍EXP及IMP使用时应当注意的事项。
一. 数据库与客户端字符集
为了保证我们使用EXP/IMP不会产生乱码问题,我们的客户端与数据库的字符集应当保持一致,数据库与客户端字符集的查询方法如下。
数据库:
SQL> select * from nls_database_parameters where parameter='NLS_LANGUAGE' or parameter='NLS_TERRITORY' or parameter='NLS_CHARACTERSET';
PARAMETER VALUE
-------------------- --------------------
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CHARACTERSET ZHS16GBK
我们知道NLS_LANG = language_territory.charset,其中language是oracle消息语言,日期中月,日显示格式;territory是货币和数字格式,日期计算格式;charset是字符集。
客户端:
SQL> host echo $NLS_LANG;或者[oracle@localhost ~]$ env |grep NLS_LANG; --Unix&Linux系统
AMERICAN_AMERICA.ZHS16GBK
C:/>echo %NLS_LANG%; --windows系统
AMERICAN_AMERICA.ZHS16GBK;
我们只要保证客户端和服务器的charset一致就不会在导入导出过程中产生乱码现象。而数据库的字符集在创建数据库的时候就指定了,一般我们很少去更改它,所有如果有不一致的情况,最简单的方法就是更改客户端的字符集。例如,在windows下直接在CMD中:set nls_lang= AMERICAN.ZHS16GBK;在linux下打开一个shell运行:export NLS_LANG=AMERICAN.ZHS16GBK。有关字符集的问题还可以参考oracle官网的FAQ:http://www.oracle.com/technology/tech/globalization/htdocs/nls_lang%20faq.htm#_Toc110410545
二. 源数据库和目标数据库字符集
我们在进行数据迁移的时候有两种字符集相关的情况,源数据库和目标数据库字符集相同和不同。如果是相同的话只要保证导出和导入时客户端字符集与数据库的相同就可以了。如果不同则在导出时客户端使用和目标数据库的字符集保持一致,而导入时进行一些设置,使用UltraEdit打开查看十六进制文件,第2-3个字节表示字符集(在linux可以使用:cat exp.dmp |od -x|head -1|awk '{print $2 $3}'|cut -c 3-6 查看),通过修改相对应的内容“骗”过导入时oracle的检查,从而达到将字符子集的dmp文件导入到字符超级的数据库中。例如我们要把一个dmp文件导入字符集为ZHS16GBK的数据库,首先查看第2-3字节的内容(假设是00 01),通过以下语句查看dmp文件的字符集是否与目标数据库的一样,不一样则修改。修改完成后统一客户端与数据库的字符集,直接导入数据库即可。
SQL> select nls_charset_name(to_number('0001','xxxx')) from dual;
NLS_CHARSET_NAME(TO_NUMBER('0001','XXXX'
----------------------------------------
US7ASCII
知道了字符集,查询所对应的十六进制数 :
SQL> select to_char(nls_charset_id('ZHS16GBK'), 'xxxx') from dual;
TO_CH
-----
354
以上的方法是在不得以的情况下使用,其本质是欺骗数据库,强行导入数据,可能造成元数据的损失,如果要确保数据的完整性,应该使用csscan扫描数据库,找出所有不兼容的字符,然后通过编写相应的脚本及代码,在转换之后进行更新,确保数据的完整性。以下是简单介绍csscan的使用:以DBA角色登录SQLPLUS运行脚本csminst.sql,然后通过运行命令csscan来扫面数据库,最后通过报告分析来确定哪些对象是需要处理的。
[oracle@test u01]$ sqlplus "/ as sysdba"
SQL> @?/rdbma/admin/csminst.sql
[oracle@test u01]$ csscan full=y fromchar=US7ASCII tochar=ZHS16GBK log=/u01/check.log capture=y array=1000000 process=2;
三. 不同版本的EXP/IMP问题
一般来说,从低版本导入高版本问题不大,麻烦的是将高版本的数据库中的数据导入低版本的,在9i以前,不同版本oracle间的EXP/IMP可以通过下面方法解决:
1. 在高版本数据库上运行底版本的catexp.sql;
2. 使用低版本的EXP来导出高版本的数据;
3. 使用低版本的IMP将数据库导入到低版本数据库中;
4. 在高版本数据库上重新运行高版本的catexp.sql脚本。
但在9i中,上面的方法并不能解决问题。如果直接使用低版本EXP/IMP会出现如下错误:
EXP-00008: orACLE error %lu encountered
orA-00904: invalid column name
这已经是一个公布的BUG,需要等到Oracle10.0才能解决,BUG号为2261722,你可以到METALINK上去查看有关此BUG的详细信息。BUG归BUG,我们的工作还是要做,在没有Oracle的支持之前,我们就自己解决。在Oracle9i中执行下面的SQL重建exu81rls视图即可。
Create or REPLACE view exu81rls
(objown,objnam,policy,polown,polsch,polfun,stmts,chkopt,enabled,spolicy)
AS select u.name, o.name, r.pname, r.pfschma, r.ppname, r.pfname,
decode(bitand(r.stmt_type,1), 0,'', 'Select,')
|| decode(bitand(r.stmt_type,2), 0,'', 'Insert,')
|| decode(bitand(r.stmt_type,4), 0,'', 'Update,')
|| decode(bitand(r.stmt_type,8), 0,'', 'Delete,'),
r.check_opt, r.enable_flag,
DECODE(BITAND(r.stmt_type, 16), 0, 0, 1)
from user$ u, obj$ o, rls$ r
where u.user# = o.owner#
and r.obj# = o.obj#
and (uid = 0 or
uid = o.owner# or
exists ( select * from session_roles where role='Select_CATALOG_ROLE')
) ;
grant select on sys.exu81rls to public;
可以跨版本的使用EXP/IMP,但必须正确地使用EXP和IMP的版本:
1. 总是使用IMP的版本匹配数据库的版本,如:要导入到817中,使用817的IMP工具。
2. 总是使用EXP的版本匹配两个数据库中最低的版本,如:从9201往817中导入,则使用817版本的EXP工具。