oracle采⽤了WE8MSWIN1252字符集乱码的问题
出现此⽂的原因是本⼈在向Oracle中插⼊中⽂的时候全部变成了问号,在解决的过程中发现是Oracle字符集的问题,从下⽂中到了完美的解决⽅案,特此记录备⽤:
测试的时候,本机oracle安装采⽤了WE8MSWIN1252字符集,⽽项⽬的要求是gbk字符集,为了防⽌以后有不同字符集数据信息导⼊导出的问题,整理以下⽂档。
修改oracle字符集新装了oracle,装为AL32UTF8格式,⽆奈⼀个⼯程导出包是ZHS16GBK格式,想了想办法转换,以下是学习
⼀、什么是oracle字符集
  Oracle字符集是⼀个字节数据的解释的符号集合,有⼤⼩之分,有相互的包容关系。ORACLE ⽀持国家语⾔的体系结构允许你使⽤本地化语⾔来存储,处理,检索数据。它使数据库⼯具,错误消息,排序次序,⽇期,时间,货币,数字,和⽇历⾃动适应本地化语⾔和平台。
  影响oracle数据库字符集最重要的参数是NLS_LANG参数。它的格式如下:
  NLS_LANG = language_territory.charset
  它有三个组成部分(语⾔、地域和字符集),每个成分控制了NLS⼦集的特性。其中:
  Language 指定服务器消息的语⾔,territory 指定服务器的⽇期和数字格式,charset 指定字符集。如:AMERICAN _ AMERICA. ZHS16GBK
  从NLS_LANG的组成我们可以看出,真正影响数据库字符集的其实是第三部分。所以两个数据库之间的字符集只要第三部分⼀样就可以相互导⼊导出数据,前⾯影响的只是提⽰信息是中⽂还是英⽂。
  ⼆、如何查询Oracle的字符集
  很多⼈都碰到过因为字符集不同⽽使数据导⼊失败的情况。这涉及三⽅⾯的字符集,⼀是oracel server端的字符集,⼆是oracle client端的字符集;三是dmp⽂件的字符集。在做数据导⼊的时候,需要这三个字符集都⼀致才能正确导⼊。
  1、查询oracle server端的字符集
  有很多种⽅法可以查出oracle server端的字符集,⽐较直观的查询⽅法是以下这种:SQL>select userenv(‘language’) from dual;
  结果类似如下:AMERICAN _ AMERICA. ZHS16GBK
(本机结果SIMPLIFIED CHINESE_CHINA.WE8MSWIN1252)
  2、如何查询dmp⽂件的字符集
  ⽤oracle的exp⼯具导出的dmp⽂件也包含了字符集信息,dmp⽂件的第2和第3个字节记录了dmp ⽂件的字符集。如果dmp⽂件不⼤,⽐如只有⼏M或⼏⼗M,可以⽤UltraEdit 打开(16进制⽅式),看第2第3个字节的内容,如0354,然后⽤ 以下SQL查出它对应的字符集:
  SQL> select nls_charset_name(to_number('0354','xxxx')) from dual;
  ZHS16GBK
oracle 时间转换  如果dmp⽂件很⼤,⽐如有2G以上(这也是最常见的情况),⽤⽂本编辑器打开很慢或者完全打不开,可以⽤以下命令(在unix主机上):
  cat exp.dmp |od -x|head -1|awk '{print $2 $3}'|cut -c 3-6
  然后⽤上述SQL也可以得到它对应的字符集。
  3、查询oracle client端的字符集
  这个⽐较简单。在windows平台下,就是注册表⾥⾯HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\HOME0\NLS_LANG 。还可以在dos窗⼝⾥⾯⾃⼰设置,⽐如:
  set nls_lang=AMERICAN_AMERICA.ZHS16GBK
  这样就只影响这个窗⼝⾥⾯的环境变量。
  在unix平台下,就是环境变量NLS_LANG。
  $echo $NLS_LANG
  AMERICAN_AMERICA.ZHS16GBK
  如果检查的结果发现server端与client端字符集不⼀致,请统⼀修改为同server端相同的字符集。
  三、修改oracle的字符集
  上⽂说过,oracle的字符集有互相的包容关系。如us7ascii就是zhs16gbk的⼦集,从 us7ascii到zhs16gbk不会有数据解释上的问题,不会有数据丢失。在所有的字符集中utf8应该是最⼤,因为它基于unicode,双字节保 存字符(也因此在存储空间上占⽤更多)。
  ⼀旦数据库创建后,数据库的字符集理论上讲是不能改变的。因此,在设计和安装之初考虑使⽤哪⼀种字符集⼗分重 要。根据Oracle的官⽅说明,字符集的转换是从⼦集到超集受⽀持,反之不⾏。如果两种字符集之间根本没有⼦集和超集的关系,那么字符集的转换是不受 oracle⽀持的。对数据库server⽽⾔,错误的修改字符集将会导致很多不可测的后果,可能会严重影响数据库的正常运⾏,所以在修改之前⼀定要确认 两种字符集是否存在⼦集和超集的关系。⼀般来说,除⾮万不得已,我们不建议修改oracle数据库server端的字符集。特别说明,我们最常⽤的两种字 符集ZHS16GBK和ZHS16CGB231280之间不存在⼦集和超集关系,因此理论上讲这两种字符集之间的相互转换不受⽀持。
  1、修改server端字符集(不建议使⽤)
  在oracle 8之前,可以⽤直接修改数据字典表props$来改变数据库的字符集。但oracle8之后,⾄少有三张系统表记录了数据库字符集的信息,只改props$表并不完全,可能引起严重的后果。正确的修改⽅法如下:
  $sqlplus /nolog
  SQL>conn / as sysdba;
以上⽅法测试不⾏,⽤scott/tiger登陆sqlplus然后connect sys/sys as sysdba,然后输⼊命令即可
  若此时数据库服务器已启动,则先执⾏SHUTDOWN IMMEDIATE命令关闭数据库服务器,然后执⾏以下命令:
  SQL>STARTUP MOUNT;
  SQL>ALTER SYSTEM ENABLE RESTRICTED SESSION;
  SQL>ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0;
  SQL>ALTER SYSTEM SET AQ_TM_PROCESSES=0;
  SQL>ALTER DATABASE OPEN;
  SQL>ALTER DATABASE CHARACTER SET INTERNAL_USE ZHS16GBK; //跳过超⼦集检测
  SQL>ALTER DATABASE national CHARACTER SET INTERNAL ZHS16GBK;
这⼀⾏不起作⽤,执⾏后出错ORA-00933: SQL 命令未正确结束,不过执⾏上⼀⾏命令已经⽣效,其他⽂章⾥未提到本⾏。
  SQL>SHUTDOWN IMMEDIATE;
  SQL>STARTUP
  2、修改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⽆法打开,就需要⽤程序的⽅法了。⽹上有⼈⽤java存储过程写了转换的程序(⽤ java存储过程的好处是通⽤性教好,缺点是⽐较⿇烦)。我在windows下测试通过。但要求oracle数据库⼀定要安装JVM选项。有兴趣的朋友可 以研究⼀下程序代码