Oracle数据库字符集问题解析 zz[2]

[入库:2005年8月18日] [更新:2007年3月24日]

本文简介:选择自 llmmysun 的 blog


sql> select * from test;

r1
--------------------
6+11

疑问1:zhs16gbk为us7ascii的超集,为什么在zhs16gbk环境下无法正常显示




这主要是因为oracle检查发现数据库设置的字符集与客户端配置字符集不同,它将对数据进行字符集的转换。数据库中实际存放的数据为182(10110110)、171(10101011)、177(10110001)、177(10110001),由于数据库字符集设置为us7ascii,它是一个7bit的字符集,存储在8bit的字节中,则oracle忽略各字节的最高bit,则182(10110110)就变成了54(0110110),在zhs16gbk中代表数字符号“6”(当然在其它字符集中也是“6”),同样过程也发生在其它3个字节,这样“东北”就变成了“6+11”。

实验结果分析三

quote:
最初由 tellin 发布
用zhs16gbk插入数据
sql> insert into test values('东北');

1 row created.

sql> select * from test;

r1
--------------------
6+11
??

sql> exit


当客户端字符集设置为zhs16gbk后向数据库插入“东北”,oracle检查发现数据库设置的字符集为us7ascii与客户端不一致,需要进行转换,但字符集zhs16gbk中的“东北”两字在us7ascii中没有对应的字符,所以oracle用统一的“替换字符”插入数据库,在这里为“?”,编码为63(00111111),这时,输入的信息实际上已经丢失,不管字符集设置如何改变(如下面引用的实验结果),第二行select出来的结果也都是两个“?”号(注意是2个,而不是4个)。

quote:

更改客户端字符集为us7ascii
d:\>set nls_lang=american_america.us7ascii

d:\>sqlplus "/ as sysdba"

无法显示用zhs16gbk插入的字符集,但可以显示用us7ascii插入的字符集
sql> select * from test;

r1
----------
东北
??


更改服务器字符集为zhs16gbk
sql> update props$ set value$='zhs16gbk' where name='nls_characterset';

1 row updated.

sql> commit;

更改客户端字符集为zhs16gbk
d:\>set nls_lang=american_america.zhs16gbk

d:\>sqlplus "/ as sysdba"

可以显示以前us7ascii的字符集,但无法显示用zhs16gbk插入的数据,说明用zhs16gbk插入的数据为乱码。

sql> select * from test;

r1
--------------------
东北
??


需要指出的是,通过“update props$ set value$='zhs16gbk' where name='nls_characterset';”来修改数据库字符集是非常规作法,很可能引起问题,在这里只是原文引用网友的实验结果。


实验结果分析四

quote:

sql> insert into test values('东北');

1 row created.

sql> select * from test;

r1
--------------------
东北
??
东北

sql> exit


由于此时数据库与客户端的字符集设置均为zhs16gbk,所以不会发生字符集的转换,第一行与第三行数据显示正确,而第二行由于存储的数据就是63(00111111),所以显示的是“?”号。

quote:

更改客户端字符集为us7ascii

d:\>set nls_lang=american_america.us7ascii

d:\>sqlplus "/ as sysdba"

无法显示数据

sql> select * from test;

r1
----------
??
??
??

疑问2:第一行数据是用us7ascii环境插入的,为何无法正常显示?


将客户端字符集设置改为us7ascii后进行select,oracle检查发现数据库设置的字符集为zhs16gbk,数据需要进行字符集转换,而第一行与第三行的汉字“东”与“北”在客户端字符集us7ascii中没有对应字符,所以转换为“替换字符”(“?”),而第二行数据在数据库中存的本来就是两个“?”号,所以虽然在客户端显示的三行都是两个“?”号,但在数据库中存储的内容却是不同的。

实验结果分析五

quote:


sql> insert into test values('东北');

1 row created.

sql> exit
更改客户端字符集为zhs16gbk
d:\>set nls_lang=american_america.zhs16gbk


d:\>sqlplus "/ as sysdba"

无法显示用us7ascii插入的字符集,但可以显示用zhs16gbk插入的字符集
sql> select * from test;

r1
--------------------
东北
??
东北
6+11

sql>
疑问3:us7ascii为zhs16gbk的子集,为何在us7ascii环境下插入的数据无法显示? [/b]


在客户端字符集设置为us7ascii时,向字符集为zhs16gbk的数据库中插入“东北”,需要进行字符转换,“东北”的zhs16gbk编码为182(10110110)、171(10101011)与177(10110001)、177(10110001),由于us7ascii为7bit编码,oracle将这两个汉字当作四个字符,并忽略各字节的最高位,从而存入数据库的编码就变成了54(00110110)、43(00101011)与49(00110001)、49(00110001),也就是“6+11”,原始信息被改变了。这时,将客户端字符集设置为zhs16gbk再进行select,数据库中的信息不需要改变传到客户端,第一、三行由于存入的信息没有改变能显示“东北”,而第二、四行由于插入数据时信息改变,所以不能显示原有信息了。

分析了这么多的内容,但实际上总结起来也很简单,要想在字符集方面少些错误与麻烦,需要坚持两条基本原则:
在数据库端:选择需要的字符集(通过create database中的character set与national character set子句指定);
在客户端:设置操作系统实际使用的字符集(通过环境变量nls_lang设置)。

例如:
character set zhs16gbk
national character set al16utf16

本文关键:Oracle数据库字符集问题解析 zz
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top