数据库管理与开发 阶梯

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

什么是Oracle字符集

Oracle字符集是一个字节数据解释的符号集合,有大小之分,有相互的包容关系。

Oracle支持国家语言的体系结构允许你使用本地化语言来存储,处理,检索数据。它使数据库工具,错误消息,排序次序,日期,时间,货币,数字和日历自动适应本地化语言和平台。

影响oracle数据库字符集最重要的参数是NLS_LANG参数。它的格式如下:

NLS_LANG = language_territory.charset

它有三个组成部分(语言、地域和字符集),每个成分控制了NLS子集的特性。其中:

Language指定服务器消息的语言,territory指定服务器的日期和数字格式,charset指定字符集。如:AMERICAN _ AMERICA. ZHS16GBK。

从NLS_LANG的组成我们可以看出,真正影响数据库字符集的其实是第三部分。所以两个数据库之间的字符集只要第三部分一样就可以相互导入导出数据,前面影响的只是提示信息是中文还是英文。

数据库的字符集

字符集在创建数据库时指定,在创建后通常不能更改,所以在创建数据库时能否选择一个正确的字符集就显得尤为重要。

在创建数据库时,我们可以指定字符集(CHARACTER SET)和国家字符集(NATIONAL CHARACTER SET)。

字符集用来存储:

CHAR, VARCHAR2, CLOB, LONG等类型数据

用来标示诸如表名、列名以及PL/SQL变量等

SQL和PL/SQL程序单元等

国家字符集用以存储:

NCHAR, NVARCHAR2, NCLOB等类型数据

一旦你的字符集选定了,数据库中能够存储的字符就受到了限制,所以你选择的字符集的应该可以容纳所有你将用到字符。字符集不同,二进制码的组合就不同。比如有一串二进制信息:1101,0110,1101,0000,1011,1001,1111,1010,按照16位双字节GBK字符集理解,可以代表“中国”两个字。如果单字节的字符集,这一串二进制代表ASC码为214、208、185、250的四个怪字符。这就是字符集的作用,就是以什么样的形式理解信息。

如何查询Oracle的字符集

很多人都碰到过因为字符集不同而使数据导入失败的情况。这涉及三方面的字符集,一是Oracel server端的字符集,二是oracle client端的字符集;三是dmp文件的字符集。在做数据导入的时候,需要这三个字符集都一致才能正确导入。

1、查询Oracle Server端的字符集

有很多种方法可以查出oracle server端的字符集,比较直观的查询方法是以下这种:

SQL>select userenv(‘language’) from dual;

结果类似如下:AMERICAN _ AMERICA. ZHS16GBK.

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

如果dmp文件很大,比如有2G以上(这也是最常见的情况),用文本编辑器打开很慢或者完全打不开,可以用以下命令(在unix主机上):

cat exp.dmp |od -x|head -1|awk '{print $2 $3}'|cut -c 3-6

然后用上述SQL也可以得到它对应的字符集。

3、查询Oracle client端的字符集

这个比较简单。在Windows平台下,就是注册表里面相应OracleHome的NLS_LANG.还可以在Dos窗口里面自己设置,比如:

set nls_lang=AMERICAN_AMERICA.ZHS16GBK

这样就只影响这个窗口里面的环境变量。在Unix平台下,就是环境变量NLS_LANG.

$echo $NLS_LANG 
AMERICAN_AMERICA.ZHS16GBK

客户端的字符集要求与服务器一致,才能正确显示数据库的非Ascii字符。如果多个设置存在的时候,alter session>环境变量>注册表>参数文件

字符集要求一致,但是语言设置却可以不同,语言设置建议用英文。如字符集是zhs16gbk,则nls_lang可以是American_America.zhs16gbk。

 

1.1、数据库需要存储的数据类型是字符集选择的首要考虑目标。
    对于只存储英文信息的数据库等来说一般采用US7ASCII或WE8ISO8859P1等单字节的字符集就比较合适,在性能和空间上也是最优,

    同样,存储了中文信息的数据库,如果采用单字节的字符集,也是不合适的。在这种情况下,数据库的字符集虽然是US7ASCII或WE8ISO8859P1编码,但里面存储的数据编码实际上却是另外的编码格式,这种不一致的情况很容易引起问题,建议不要这样使用。ORACLE提供了很多种类的字符集供客户选择,就是要满足各种文字不同的编码需要。

1.2、字符集的选择需要优先考虑应用程序的需要。
    目前出于国际化的需要,软件需要可以对不同的语言文字进行处理,尤其一个系统中需要容纳多种语言文字的时候,一般都会采用Unicode这样的通用解决方案,即使会有一些空间和运行效率的损失也是值得的。此时数据库字符集建议可以采用AL32UTF8或UTF8编码。

客户在应用程序中输入数据,此时数据的编码格式是由客户操作系统的区域及语言设置决定的,如在简体中文XP的环境下,输入的中文编码属于GBK编码。在客户输入结束后,程序首先判断客户的本地环境,并把编码转换成UNICODE,并通过NET传送到服务器端。由于客户端与服务器数据库的字符集均为UTF8格式,ORACLE在传送过程中不会进行字符转换,直接把数据按UTF8格式存储到数据库中。查询时是一个反向的过程,应用程序从数据库中取出UTF8编码的数据,再由应用程序根据客户的本地环境,把UTF8编码的数据转换成客户本地的编码格式,最后把结果数据显示给客户。此方案的关键在于应用程序要能很好的支持UNICODE编码,编码的转换由应用程序来负责,数据库只是提供了一个数据存储功能。
    对于部分程序来说,由于对UNICODE支持不够,没有提供编码的转换功能,则可以使用ORACLE提供的字符集转换功能来实现同样的目的。客户在应用程序中输入数据,此时数据的编码格式是由客户操作系统的区域及语言设置决定的,如在简体中文XP的环境下,输入的中文编码属于GBK编码。在客户输入结束后,程序直接把数据并通过NET传送到服务器端。由于客户端与服务器数据库的字符集不一致,因此ORACLE会把客户端的编码转换成UTF8格式,再把数据按UTF8格式存储到数据库中。这种方案的优点就是程序可以不用支持UNICODE,由ORACLE数据库自动进行转换。由于数据库的字符集为UTF8,是其它字符集的超集,因此在转换过程中不会发生数据丢失的情况。对于英文的字符符号,在UTF8中使用单字节存储,转换的工作量很小,可以忽略,而对于一些亚洲字符集,在UTF8中一般需要两到三个字节存储,需要的数据库空间增加,而且转换的工作量也相对大一些,性能会有一些损失。

备注:

对于Windows的简体中文平台,缺省的字符集是ZHS16GBK

英文平台:缺省的字符集是WE8ISO8859P1  西欧语言

支持多语言平台:AL32UTF8

 

二. 字符集的更改

数据库创建以后,如果需要修改字符集,通常需要重建数据库,通过导入导出的方式来转换。

我们也可以通过以下方式更改 转换字符集,数据库应该在RESTRICTED模式下进行.

通过修改props$的方式更改字符集在Oracle7之后是一种极其危险的方式,应该尽量避免。

我们又知道,通过ALTER DATABASE CHARACTER SET更改字符集虽然安全可靠,但是有严格的子集和超集的约束,实际上我们很少能够用到这种方法。实际上Oracle还存在另外一种更改字符集的方式.

 

SQL> 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 session set events '10046 trace name context forever,level 12';

SQL> alter database character set INTERNAL_USE ZHS16GBK

和之前ALTER DATABASE CHARACTER SET操作的内部过程是完全相同的,也就是说INTERNAL_USE提供的帮助就是使Oracle数据库绕过了子集与超集的校验.

这一方法在某些方面是有用处的,比如测试;应用于产品环境大家应该格外小心,最好复制一份数据库出来使用,要不除了你以外,没有人会为此带来的后果负责:

 

对于DBA来说,有一个很重要的原则就是:不要把你的数据库置于危险的境地!

这就要求我们,在进行任何可能对数据库结构发生改变的操作之前,先做有效的备份,很多DBA没有备份的操作中得到了惨痛的教训。

 

三、导出导入与转换

导入导出是我们常用的一个数据迁移及转化工具,因其导出文件具有平台无关性,所以在跨平台迁移中,最为常用。

在导出操作时,非常重要的是客户端的字符集设置,也就是客户端的NLS_LANG设置。

NLS_LANG参数由以下部分组成:

NLS_LANG=<Language>_<Territory>.<Clients Characterset>

language:指定oracle消息使用的语言,日期中日和月的显示。

Territory:指定货币和数字的格式,地区和计算星期及日期的习惯。

Characterset:控制客户端应用程序使用的字符集。通常设置或者等于客户端(如Windows)代码页,或者对于unicode应用设置为UTF8,在Windows上查看当前系统的代码页可以使用chcp命令

提示:NLS_LANG的定义的所有组件都是可选的,任何不指定项目使用其默认值。如果指定领土或字符集,则必须包括上述分隔[下划线(_的领土),句号(.)的字符集]。否则,该值分析作为一种语言的名称。

例如,只设置NLS_LANG的领土的一部分,使用以下格式:NLS_LANG的= _JAPAN

查看客户端NLS_LANG设置可以使用以下方法:

Windows上查看:在[HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\KEY_OraDb10g_home1] NLS_LANG键值

临时会话中设置:set %NLS_LANG%=SIMPLIFIED CHINESE_CHINA.ZHS16GBK

Linux上查看:echo $NLS_LANG

临时会话中设置:在命令窗口 export NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK

 

四、修改导出的文件字符集

我们知道在导出文件中,记录着导出使用的字符集id,通过查看导出文件头的第2、3个字节,我们可以找到16进制表示的字符集ID,在Windows上,我们可以使用UltraEdit等工具打开dmp文件,查看其导出字符集::修改dmp文件,如果dmp文件太大,如大于500M,可使用anysql的工具更改dmp文件中的字符集 — dmp2utf8,链接在此http://www.anysql.net/tools/new_tool_dmp2utf8.html

 

在Unix上我们可以通过以下命令来查看:

cat expdat.dmp | od -x | head

 

Oracle提供标准函数,对字符集名称及ID进行转换:

SQL> select nls_charset_id('ZHS16GBK') from dual;

NLS_CHARSET_ID('ZHS16GBK')

--------------------------

                       852

 

SQL> select nls_charset_name(852) from dual;

NLS_CHAR

--------

ZHS16GBK

 

SQL> select to_char(nls_charset_id('ZHS16GBK'), 'xxxx') from dual;  --十进制转换十六进制:

TO_CHAR('8

----------

  0354

 

对应上面的图中第2、3字节,我们知道该导出文件字符集为ZHS16GBk.

 

五、与字符集相关的问题分析
5.1、在UTF8环境下运行SQL语句报错的问题:
    SQL*PLUS工具不提供编码自动转换的功能,当数据库字符集为UTF8,客户端的NLS_LANG如果也是UTF8,那么在SQL*PLUS中运行SQL语句时,语句全是英文,不会出现问题,如果语句包含了中文或其它一些特殊字符,SQL语句运行时就会报错。对于返回的含中文的结果,SQL*PLUS也会显示乱码。造成此错误的原因在于当SQL语句中包含汉字等一些特殊字符时,由于这些字符的编码属于GBK,ORACLE没有进行字符转换,而是直接把SQL语句送到服务器上进行解析。此时服务器的字符集是UTF8,因此它按UTF8编码格式对SQL语句中GBK编码的字符解析时就会产生错误。如果把客户端的NLS_LANG设置为本地环境的字符集,如ZHS16GBK,此时可以直接在SQL*PLUS中输入包含中文的SQL语句,ORACLE在把SQL语句提交到服务器时会自动转换成UTF8编码格式,因此SQL语句可以正常运行。对于英文字母,由于它在UTF8中的编码数值采用的还是ASCII的编码数值,因此英文字母可以直接使用而不需要转换,这就是如果SQL语句或输出结果全是英文时不会出现错误的原因。正确的做法是先把需要运行的SQL做成脚本文件,用代码转换工具把它转换成UTF8编码格式的文件,(注意!XP中的记事本是提供了代码转换功能的,可以在保存文件或选择文件另存为的时候,弹出的对话框最后一项,编码,选择UTF8,再保存,即可把文件转换成UTF8编码格式)。完成后用IE打开这个脚本,选择编码-》UTF8,观察此时SQL脚本是否含有乱码或“?”符号。如果没有,说明编码格式已经是UTF8了,此时在SQL*PLUS中运行这个脚本就不会产生错误了。运行结束后,输出的结果中如果包含中文,需要把结果SPOOL输出到一个文件中,然后用代码转换工具把这个结果文件由UTF8转换成本地编码格式,再用写字板打开,才能看到正常显示的汉字。由于IE具有代码转换功能,因此也可以不用代码转换工具,直接在IE中打开输出的结果文件,选择UTF8编码,也能正常显示含中文的结果文件。

.2、数据库出现乱码的问题:
    数据库出现乱码的问题主要和客户的本地化环境,客户端NLS_LANG设置,服务器端的数据库字符集设置这三者有关,如果它们的设置不一致或者某个设置错误,就会很容易出现乱码,下面我们简要介绍以下几种情况:
5.2.1、数据库字符集设置不当引起的乱码:
    例如:一个存储简体中文字符的数据库,它的字符集选用了US7ASCII,当它的客户端NLS_LANG也选用US7ASCII时,这个系统单独使用是没有问题的,因为两者设置一致,因此ORACLE不会进行字符集的转换,客户输入的GBK码被直接在数据库中存储起来,当查询数据时,实际客户端取出来的数据也是GBK的编码,因此显示也是正常的。但当其它的系统需要从这个数据库取数据,或者它的数据要EXP出来,IMP到其它数据库时,问题就会开始出现了。其它系统的字符集一般是ZHS16GBK,或者其它系统客户端的NLS_LANG设置为ZHS16GBK,此时必然会产生字符集的转换。虽然数据库字符集设置为US7ASCII,但我们知道,实际存储的数据编码是ZHS16GBK的。可惜ORACLE不会知道,它会把存储的ZHS16GBK编码数据当作US7ASCII编码的数据,按照US7ASCII转换成ZHS16GBK的转换算法进行转换,可以想象,这种情况下,乱码的产生是必然的。

4.2.2、数据库字符集与客户端NLS_LANG设置不同引起的乱码:
    例如:对于一个需要存储简体文信息的数据库来说,它的字符集设置和客户端NLS_LANG设置一般可以使用ZHS16GBK编码。但是如果数据库字符集选用了UTF8的话,也是可以的,因为ZHS16GBK编码属于UTF8的子集。ORACLE在数据库与客户端进行数据交换时自动进行编码的转换,在数据库中实际存储的也是UTF8编码的数据。此时其它数据库和此数据库也可以正常的进行数据交换,因为ORACLE会自动进行数据的转换。在实际使用中,遇到过繁体XP的字符集ZHT16MSWIN950转换成AL32UTF8字符集时,一些特殊的字符和个别冷僻的汉字会变成乱码。后来证实是XP需要安装一个字库补丁软件,最后顺利解决此问题。

4.2.3、客户端NLS_LANG与本地化环境不同引起的乱码:
    一般情况下,客户端NLS_LANG与本地化环境采用了不同的字符集会出现乱码,除非本地化环境的字符集是客户端NLS_LANG设置字符集的子集。如果把客户端NLS_LANG设置为UTF8就属于这种情况,由于目前还没有可以直接使用UNICODE字符集的操作系统,因此客户本地化环境使用的字符集只能是某种语言支持的字符集,它属于UTF8的子集。下面我们就着重讨论这种情况。
    虽然目前WINDOWS的内核是支持UNICODE的,但是WINDOWS并不支持直接显示UNICODE编码的字符,而且它并不知道目前的字符采用了何种字符集,所以默认情况下,它使用缺省的代码页来解释字符。因此,对于其它类型的编码,需要先进行转换,变成系统目前的缺省代码页支持的字符集才能正常使用。
    WINDOWS中的缺省代码页是由控制面板设置中的语言及区域的选择所决定的,属于客户本地化的环境设置。简体中文WINDOWS的字符编码就是GBK,它的缺省代码页是936。对于其它非WINDOWS的操作系统,我们可以把它们目前缺省使用的字符集作为用户的本地化环境设置。另外,我们使用的大部分工具,如写字板,SQL*PLUS等,它们没有提供编码转换功能,因此在客户端直接输入或查询数据往往都会遇到乱码的问题,必须由应用程序或一些工具去做编码的转换,才能保证正常的使用。

 

五、实验操作:只为理解字符集Characterset ,关于乱码。

环境:

客户端Windows中文简体系统,Oracle10G,客户端字符集为:

服务器与客户机为同一PC机,数据库字符集为:AL32UTF8 国家字符集为:UTF8

说明:关于客户端与服务端字符集查看请看上面的文档。

 

参考资料:

http://www.oracle.com/technology/tech/globalization/htdocs/nls_lang%20faq.htm#_Toc110410543 nls_lang faq

http://www.eygle.com/special/ 查找字符集部分

http://www.itpub.net/viewthread.php?tid=838447&highlight=%D7%D6%B7%FB%BC%AF  搞懂oracle字符集 

 

posted on 2009-03-21 16:47  zhou__zhou  阅读(1928)  评论(0编辑  收藏  举报