现象:windows系统dos环境下执行dmp脚本时,中文信息显示为????乱码
解决办法1:统一字符集
(1)查询数据库字符集:一是Oracle server端的字符集,二是Oracle client端字符集,三是dmp文件字符集;三者统一才能正确导入;
(1-1)查询server端字符集:<1> SQL>select userenv('language') from dual;
结果类似为:AMERICAN_AMERICA.ZHS16GBK
<2> SQL>select * from nls_database_parameters; 来源于prop$,表示数据库的字符集。
(1-2)查询client端字符集:
windows平台:<1>注册表中相应OracleHome的NLS_LANG;
<2>dos窗口中设置,SQL>select * from nls_instance_parameters; 来源于V$parameter,表示客户端字符集的设置,可能是参数文件,环境变量或注册表。
unix平台:修改环境变量NLS_LANG
$echo $NLS_LANG
AMERICAN_AMERICA.ZHS16GBK
(1-3)查询dmp文件字符集:dmp文件的第2和第3个字节记录了dmp文件的字符集。
<1>dmp文件较小(几M或几十M),用UltraEdit打开(16进制方式),看第2个和第3个字节的内容,如0354,查询:SQL>select nls_charect_name(to_number('0354','xxxx')) from dual;
结果类似:ZHS16GBK
<2>Zdmp文件较大(比如2G),用文本编辑器打开很慢或者完全打不开,可以以下命令(在nuix主机上):
cat exp.dmp |od -x|head -1|awk '{print $2 $3}'|cut -c 3-6,然后用上述sql也可得到对应字符集。
(1-4)查询会话字符集:
SQL>select * from nls_session_parameter; 来源于V$nls_parameter,表示会话自己的设置,可能是会话的环境变量或者alter session完成,如果会话没有特殊设置,将于nls_instance_parameter一致。
备注:客户端字符要求与服务端一致,才能正确显示数据库的非Ascii字符。如果多个设置存在的时候,alter session>环境变量>注册表>参数文件 字符集要求一致,但语言设置可以不同,语言设置建议用英文。如字符集是ZHS16QBK,则nls_lang可以是AMERICAN_AMERICA.ZHS16GBK。
(2)修改字符集:
oracle字符集有相互包容关系,如us7asc就是zhs16gbk的子集,从us7asc到zhs16gbk不会有数据解释上的问题,不会有数据丢失。在所有字符集中utf8应该是最大,因为它是基础Unicode双字节保存字符。
一旦数据库创建后,数据库的字符集理论上讲是不能改变的。因此,在设计和安装之初考虑使用哪一种字符集十分重要。根据Oracle的官方说明,字符集的转换是从子集到超集受支持,反之不行。如果两种字符集之间根本没有子集和超集的关系,那么字符集的转换是不受oracle支持的。对数据库server而言,错误的修改字符集将会导致很多不可测的后果,可能会严重影响数据库的正常运行,所以在修改之前一定要确认两种字符集是否存在子集和超集的关系。一般来说,除非万不得已,我们不建议修改oracle数据库server端的字符集。特别说明,我们最常用的两种字符集ZHS16GBK和ZHS16CGB231280之间不存在子集和超集关系,因此理论上讲这两种字符集之间的相互转换不受支持。
(2-1)修改server端字符集(不建议使用)
SQL>sqlplus /nolog
SQL>conn / as sysdba
SQL>shutdown immediate; --关闭数据库服务器
SQL>startup mount;
SQL>alert system enable restricted session;
SQL>alert system set job_queue_processes=0;
SQL>alert system set aq_tm_processes=0;
SQL>alert database open;
SQL>alert database character set zhs16gbk;
SQL>alert database national character set zhs16gbk;
SQL>shutdown immediate;
SQL>startup;
备注:如果没有大对象,在使用过程中进行语言转换没有什么影响,(切记设定的字符集必须是oracle支持,不然不能start),按上面的做法就可以,但是可能会出现‘ORA-12717:Cannot ALERT DATABASE NATIONAL CHARACTER SET when NCLOB data exists’这样的提示信息;
解决办法1:利用INTERNAL_USE 关键字修改区域设置;
SQL>shutdown immediate;
SQL>startup mount exclusive;
SQL>alert system enable restricted session;
SQL>alert system set job_queue_processes=0;
SQL>alert system set aq_tm_processes=0;
SQL>alert database open;
SQL>alert database national character set internal_use utf8;
SQL>shutdown immediate;
SQL>startup;
如果按上面的做法做,national charset的区域设置就没有问题;SQL>SQL>SQL>SQL>SQL>
解决办法2:利用re-create,但是re-create有点复杂,所以请用internal_use;
(2-2)client修改字符集:
比如set nls_lang=AMERICAN_AMERICA.ZHS16GBK;这个只影响dos窗口里的环境变量;
(2-3)修改dmp文件字符集
上文说过,dmp文件的第2第3字节记录了字符集信息,因此直接修改dmp文件的第2第3字节的内容就可以‘骗’过oracle的检查。这样做理论上也仅是从子集到超集可以修改,但很多情况下在没有子集和超集关系的情况下也可以修改,我们常用的一些字符集,如US7ASCII,WE8ISO8859P1,ZHS16CGB231280,ZHS16GBK基本都可以改。因为改的只是dmp文件,所以影响不大。
具体的修改方法比较多,最简单的就是直接用UltraEdit修改dmp文件的第2和第3个字节。
比如想将dmp文件的字符集改为ZHS16GBK,可以用以下SQL查出该种字符集对应的16进制代码:
SQL> select to_char(nls_charset_id('ZHS16GBK'), 'xxxx') from dual;
0354
然后将dmp文件的2、3字节修改为0354即可。
如果dmp文件很大,用ue无法打开,就需要用程序的方法了
小知识:
影响oracle数据库字符集最重要的参数是NLS_LANG参数。
它的格式如下: NLS_LANG = language_territory.charset
它有三个组成部分(语言、地域和字符集),每个成分控制了NLS子集的特性。
Language 指定服务器消息的语言,territory 指定服务器的日期和数字格式,charset 指定字符集。如:AMERICAN _ AMERICA. ZHS16GBK
从NLS_LANG的组成我们可以看出,真正影响数据库字符集的其实是第三部分。
所以两个数据库之间的字符集只要第三部分一样就可以相互导入导出数据,前面影响的只是提示信息是中文还是英文。
参考博客 http://blog.csdn.net/dream19881003/article/details/6800056
http://blog.csdn.net/rbyyyblog/article/details/7397076