2008年12月10日星期三

再探ORACLE数据库字符集乱码问题

一 服务器字符集

如前文所述,ORACLE数据库有数据库字符集和国家语言字符集两种。数据库字符集用于存储char,varchar2,clob,long等数据类型,用来标示表名,列名,sql和PLSQL存储单元等。而国家字符集通常只用来存储nvarchar2,nchar,vclob这些数据类型。建库时就已经决定了数据库字符集和国家语言字符集。对于简体中文操作系统,如果安装数据库的时候没有特别指定,默认字符集就是ZHS16GBK。



ORACLE数据库中字符集的命名遵循以下规则:
Language\bit size\encoding
ZHS 16 GBK
当然有一些字符集命名不在此例:
AL16UTF16,其中AL代表ALL,指的是all language。

二 客户端字符集

NLS_LANG参数由以下组成:
NLS_LANG=language_Territory.client_characterset
language指定ORACLE所用的提示语言,日期中月份和日的显示。
Territory指定货币和数字格式,地区和计算星期及日期的习惯。
client_characterset控制客户端应用程序的字符集。
如果是在windows平台,可以使用chcp查看系统页代码,比如中文winxp操作系统就会显示“活动的代码页:936”。
在sqlplus里可以使用alter session set nls_lang=...来修改这个参数,在sql developer中,需要在tools->preference->database->NLS_PARAMETERS来修改。

三 乱码产生

乱码的产生是由于下面三个字符集引起的:
数据库字符集
客户端字符集
客户端NLS_LANG参数的设置

(1)当客户端字符集和数据库字符集不同,同时NLS_LANG不同于服务器字符集。
在这种情况下存在两种可能性:
a 客户端输入的字符在NLS_LANG中没有对应的字符,这时会以?来代替。
b 客户端输入的字符在NLS_LANG中对了其他字符,传递给数据库后,字符被存储下来,但是元数据丢失,因此数据库中存储的字符不再代表客户输入的字符。(注意:这个过程不可逆,因为通常情况下我们不知道客户端字符集)

(2)NLS_LANG和服务器字符集相同时
在这种情况下,数据库不用对客户端传递过来的数据进行编码转换,直接存入数据库。如果客户端传过来的字符集在数据库中有正确的对应关系,则存储正确,否则就被替换为?,于是就产生了乱码。
比如如果数据库字符集是WE8MSWIN1252时,即使NLS_LANG设置相同,数据库也不能正确的存储汉字。


四 常用的转换方法
通常使用dblink,并组合utl_raw.cast_to_raw以及utl_raw.cast_to_varchar2来转化,具体步骤请参考ORACLE公司的文档。

没有评论: