ORACLE字符集问题的分析
2019-08-06张娈
张娈
1.问题提出背景
随着信息化建设的不断深入,各大港口公司都运用应用系统来辅助生产和管理,数据库是其中不可或缺的部分,占有重要的地位。在各大数据库产品中,选择 ORACLE 作为数据库管理平台的用户比较多。 ORACLE 不论是数据库管理能力还是安全性都是无可非议的,但是,它在汉字信息的显示方面确实给我们带来不少麻烦,笔者就经常遇到有关数据库汉字显示的问题,尤其是在数据库导入导出的时候,主要现象是把汉字显示为不可识别的乱码(一般是“?”),造成原来大量信息无法使用。本文将就这一问题产生的原因和解决办法进行一些探讨。
2.ORACLE字符集概述
2.1 字符集定义
字符集是一个字节数据的解释的符号集合,有大小之分有相互的包括关系,如US7ASCII就是ZHS16GBK的子集,从US7ASCII到ZHS16GBK不会有数据解释上的问题,不会有数据丢失,Oracle对这种问题也要求从子集到超集的导出受支持,反之不行。字符集决定数据库所支持的语言标准,也就是说,数据库支持中文、日文或是英文不是有操作系统平台决定的,而是由字符集决定的。
字符集不仅需在服务器端存在,而且客户端也必须有字符集注册。服务器端字符集是在安装数据库时指定的,字符集登记信息存储在数据库字典的 V$NLS_PARAMETERS 表中;客户端字符集是在系统注册表中登记的。要在客户端正确显示数据库汉字信息,首先必须使服务器端的字符集与客户端的字符集一致;其次是加载到数据库的数据字符集必须与服务器端字符集一致。影响数据库字符集最重要的参数是NLS_LANG参数。它的格式如下:NLS_LANG = language_territory.charset。它有三个组成部分(语言、地域和字符集),每个成分控制了NLS子集的特性。其中: Language 指定服务器消息的语言,也就是sqlplus的程序的显示字体,一般常用SIMPLIFIED CHINESE,American America;Territory 指定服务器的日期和数字格式;Charset是字符集的设定。常用的一些字符集有UTF8,US7ASCII,ZHS16GBK,AL32UTF8。
从NLS_LANG的组成我们可以看出,真正影响数据库字符集的其实是第三部分。所以两个数据库之间的字符集只要第三部分一样就可以相互导入导出数据,前面影响的只是提示信息是中文还是英文。
2.2oracle字符集的查询方法
2.2.1查询oracle服务器端的字符集 SQL> select * from nls_database_parameters;
2.2.2查询dmp文件的字符集 用Oracle的exp工具导出的dmp文件也包含了字符集信息,dmp文件的第2和第3个字节记录了它的字符集。如果文件不大,比如只有几M或几十M,可以用UltraEdit打开(16进制方式),看第2、第3个字节的内容,如0001,然后用以下SQL查出它对应的字符集:SQL> select nls_charset_name(to_number('0001','xxxx')) from dual;如果文件很大,比如有2G以上(这也是最常见的情况),用文本编辑器打开很慢或者完全打不开,可以用以下命令(在unix主机上):$ cat a.dmp |od -x| head 其中,a.dmp是需要查看字符集的dmp文件。
2.2.3查询Oracle Client端的字符集
在Windows中,决定客户端字符集的参数nls_lang定义在Windows系统的注册表里,如果要重新定义,可以直接修改注册表。运行注册表,选择” HKEY_LACAL_MACHINE”→”SOFTWARE”→”ORACLE”→”HOME0”,查看里面的NLS_LANG数据项。还可以在Dos窗口里面自己设置,比如:set nls_lang=AMERICAN_AMERICA. US7ASCII 这样就只影响这个窗口里面的环境变量。
3.幾种乱码问题的解决方法
3.1服务器端字符集与客户字端字符集不同,但与加载数据字符集一致。
解决方法:设置客户端字符集与服务器端字符集一致。首先查看服务器端字符集,然后按照服务器端字符集对客户端进行配置。修改注册表信息,将NLS_LANG数据项值改为与服务器端相同的字符集。
3.2服务器端字符集与客户端字符集相同,与加载数据字符集不一致。
这类问题一般发生在服务器数据库版本升级或重新安装系统时选择了与原来服务器端不同的字符集,而恢复加载的备份数据仍是按原字符集导出,或者加载从其它使用不同字符集的数据库导出数据的情况。这两种情况中,不管服务器端和客户端字符集是否一致都无法正常显示汉字。解决方法:强制将加载数据的字符集改为与服务器端字符集一致。
方法一:强行修改服务器端数据库当前字符集。在用Imp命令加载数据前,先在客户端用sql*plus以DBA 用户登录,执行 SQL > create database character set US7ASCII ;你会发现语句执行过程中,会出现错误提示信息,此时不用理会,实际上数据库的字符集已被强行修改为US7ASCII,接着用imp命令装载数据。等数据装载完成以后,关闭数据库,再启动数据库,用合法用户登录数据库,在 sql> 命令提示符下,查询数据库字符集,可以看到其已复原,这时再查看有汉字字符数据的表时,汉字已能被正确显示。
方法二:利用数据格式转储,避开字符集限制。这种方法主要用于加载外来数据库的不同字符集数据。其方法如下:先将数据加载到具有相同字符集的服务器上,然后用转换工具卸出为access格式数据库,再用转换工具转入到不同字符集的数据库中,这样就避免了字符集的困扰。
3.3服务器端字符集与客户端字符集不同,与输入数据字符集不一致。
这种情况是在客户端与服务器端字符集不一致时,从客户端输入了汉字信息。输入的这些信息即便是把客户端字符集更改正确,也无法显示汉字。对于这种情况,没有很好的办法,只能先把客户端与服务器端字符集匹配一致后,重新录入数据。
通过上面的了解,我们知道导致在后期使用数据库是出现种种关于字符集的问题,多半是由于在数据库设计、安装指出没有很好地考虑到以后的需要,所以,我们完全可以通过在服务器和客户端使用相同的字符集来避免由此类问题引出的麻烦。
4.应注意的问题
一旦数据库创建后,数据库的字符集理论上讲是不能改变的。因此,在设计和安装之初考虑使用哪一种字符集十分重要。根据Oracle的官方说明,字符集的转换是从子集到超集受支持,反之不行。如果两种字符集之间根本没有子集和超集的关系,那么字符集的转换是不受支持的。对数据库而言,错误的修改字符集将会导致很多不可测的后果,可能会严重影响数据库的正常运行,所以在修改之前一定要确认两种字符集是否存在子集和超集的关系。一般来说,除非万不得已,我们不建议修改数据库服务器端的字符集。在10g数据库中,客户端字符集必须与数据库和行字符集类型一致,否则汉字将出现乱码;如果要将早期数据库中的数据移入到9i、10g中,由于原始数据字符集问题,新的数据库核心必须使用早期数据库核心字符集类型,客户端也要保持与早期核心字符集一致。