再论Oracle字符集

这篇博客的目的要搞清以下两个问题:
1. 如何正确设置NLS_LANG?
2. 如果正确使用国家字符集?

参考: Character Sets & Conversion - Frequently Asked Questions [ID 227330.1]

1. 如何正确设置NLS_LANG
metalink上有两篇文章是专门讨论如何设置NLS_LANG,一篇针对Windows系统,一篇针对Unix系统:
The correct NLS_LANG in a Windows Environment [ID 179133.1]
The correct NLS_LANG setting in Unix Environments [ID 264157.1]
文中提到了设置NLS_LANG的几个关键点:

* Setting the NLS_LANG to the characterset of the database (NLS_CHARACTERSET) MAY be correct but IS NOT ALWAYS correct. Please DO NOT assume that NLS_LANG needs to be ALWAYS the same as the database characterset. THIS IS NOT TRUE.

* The characterset defined with the NLS_LANG parameter does NOT CHANGE your client's characterset, it is used to let Oracle know what characterset you are USING on the client side, so Oracle can do the proper conversion.

我的结论是:NLS_LANG只和客户端终端的字符集相关,如果客户端终端的字符集和数据库字符集间无法正确转换,则应该首先改变客户端终端的字符集,而不是简单地把NLS_LANG设为和数据库字符集一样。

下面以Window XP为例子设置NLS_LANG:

1) 怎么知道我的客户端终端的字符集?
Windows上有两种类型的终端ANSI 和OEM,ANSI终端指的是图形化的终端(如sqlplusW.exe),而OEM终端指的是dos/cmd等(如sqlplus.ext)。
我们打开cmd,输入chcp(这里是OEM终端):
C:\Documents and Settings\a105024\Desktop>chcp
Active code page: 936
可以查出code page为936,再到微软的官方网站:(http://msdn.microsoft.com/en-gb/goglobal/bb896001.aspx )查询936所对应的Locale是Chinese:

LCID
trans.gif
Culture Identifier
Culture Name Locale
trans.gif
Language Country/Region
Language Local
language name
ANSI
codepage
OEM
codepage
Country or Region name abbreviation * Language name abbreviation **

0x0804zh-CNChinese (People's Republic of China)Chinese中文(中华人民共和国)936936CHNCHS
0x0004
zh-CHSChinese (Simplified)Chinese中文(简体)936936TWNCHS
0x1004zh-SGChinese (Singapore)Chinese中文(新加坡)936936SGPZHI

再到文章 The correct NLS_LANG in a Windows Environment [ID 179133.1]  point 7 里可以查出Chinese (PRC)对应的字符集为ZHS16GBK ,因此我们应该把NLS_LANG最终设为SIMPLIFIED CHINESE_CHINA.ZHS16GBK

2) 如何修改客户端终端字符集?
如果客户端终端字符集不支持中文,那么我们需要修改操作系统配置使得它支持中文,可参考该文档修改:

How to change the ANSI Code Page (ACP) on Windows NT 4.0 and Windows 2000 / XP [ID 199926.1]

2. 如果正确使用国家字符集?

The National Character Set ( NLS_NCHAR_CHARACTERSET ) in Oracle 9i, 10g and 11g [ID 276914.1]
Character Sets & Conversion - Frequently Asked Questions [ID 227330.1]  Point 14

Oracle针对国家字符集引入了一个新的参数:ORA_NCHAR_LITERAL_REPLACE,该参数默认为false,表示任何从客户端传过来的NCHAR类型的字符先转换为数据库字符集,再转换为国家数据库字符集,而把该参数设为true后,从客户端传过来的NCHAR类型的字符直接转换为国家字符集存储,因此要想正确存储NCHAR字符集,必须得把该参数设为TRUE。

注意:该参数只有在10gr2或以上的数据库才有作用(即Oracle server和Oracle client最低要10gr2)!

3. 实验

Test
 Code page NLS_LANG ORA_NCHAR_LITERAL_REPLACENLS_CHARACTERSET
NLS_NCHAR_CHARACTERSET
Input
Output
Dump
1
936
 SIMPLIFIED CHINESE_CHINA.ZHS16GBK
TRUE
WE8ISO8859P1
UTF8
N'中'
d6,d0
 Typ=1 Len=3 CharacterSet=UTF8: e4,b8,ad
2
936
 SIMPLIFIED CHINESE_CHINA.ZHS16GBK
 FALSEWE8ISO8859P1
UTF8
N'中'
d6,d0

Typ=1 Len=2 CharacterSet=UTF8: c2,bf
3
936
AMERICAN_AMERICA.WE8ISO8859P1
TRUE
WE8ISO8859P1
UTF8
N'中'
d6,d0
Typ=1 Len=4 CharacterSet=UTF8: c3,96,c3,90
4
936
AMERICAN_AMERICA.WE8ISO8859P1
 FALSEWE8ISO8859P1
 UTF8N'中'
d6,d0
Typ=1 Len=4 CharacterSet=UTF8: c3,96,c3,90

实验解释:

实验1(客户端查询正确,数据库存储正确): ‘中’在ZHS16GBK下的编码是2字节的‘d6,d0',由于ORA_NCHAR_LITERAL_REPLACE参数为TRUE,因此直接把ZHS16GBK的'中'转换为国家字符集UTF8对应的编码'e4,b8,ad’ (中文字符在UTF8中为3字节的编码);
实验2(客户端查询错误,数据库存储错误):‘中’在ZHS16GBK下的编码是2字节的‘d6,d0',由于ORA_NCHAR_LITERAL_REPLACE参数为FALSE,因此首先尝试把ZHS16GBK的‘中’转换为数据库字符集 WE8ISO8859P1 的'中',但是由于 WE8ISO8859P1 不支持中文字符,会用一个1字节的替代字符表示原来2字节的字符。接着又由于是NCHAR字符,故Oracle把该1字节的替代字符又转换成UTF8两字节的字符(c2,bf);
实验3,4 (客户端查询正确,数据库存储错误):当NLS_LANG和数据库字符集一样时,不管ORA_NCHAR_LITERAL_REPLACE怎么设置,数据库不转换直接接收'中‘在ZHS16GBK下的编码'd6,d0',此时由于数据库时1字节的编码 WE8ISO8859P1 ,因此会把它当做是两个字符看待。接着又由于是NCHAR字符,故Oracle把这两个 WE8ISO8859P1 下1字节的两个字符转换重UTF8下 2字节的两个字符(c3,96,cd,90)。
特别要注意实验3,4的情况,虽然客户端查询显示是正确的,但实际上数据库存储是错误的,这时只要对该字符做一个length操作,便一目了然了:

select length(caption) from jars.NAVIGATEMENU_YJ where id =2013;
length(caption)
-------------------
2

我们的本意是存进'中’一个字符,结果数据库里实际寸的是两个字符,因此这种情况要特别注意,如果你只是为了显示(如报表)用,图方便可以把NLS_LANG设为和数据库字符集一样,但是一旦你要在数据库层面对字符进行操作,则肯定会出错,且错误会很诡异。


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/26277071/viewspace-710454/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/26277071/viewspace-710454/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值