SQL> select * from v$nls_parameters where parameter='NLS_CHARACTERSET'; PARAMETER VALUE ------------------------------ ----------------- NLS_CHARACTERSET AL32UTF8
2. 客户端操作系统字符集:即客户端操作系统以哪种字符编码存储字符。
如果是Windows,可以使用chcp命令获得代码页(code page):
C:\Users\xianzhu>chcp Active code page: 936
如果是Linux,字符集在/etc/sysconfig/i18n设置:
LANG="zh_CN.GB2312" (指定当前操作系统的字符集) SUPPORTED="zh_CN.GB2312"(指定当前操作系统支持的字符集) SYSFONT="lat0-sun16"(指定当前操作系统的字体)
3. 客户端NLS_LANG参数:该参数用于向Oracle指示客户端操作系统的字符集。
有了以上3个基本概念之后,我来阐述一下Oracle字符集转换的基本原则:
几种常见情况分析
下面先看一个例子,再透过现象看本质,我们会针对这个例子进行分析。
该例子如下:
1. 数据库字符集为Unicode(UTF-8编码) 我们的数据库版本是10.2.0.4.0,数据库字符集是: SQL> select * from v$nls_parameters where parameter='NLS_CHARACTERSET'; PARAMETER VALUE ---------------------------------------- ------------------------------ NLS_CHARACTERSET AL32UTF8 2. 客户端操作系统字符集为代码页936(字符集为ZHS16GBK) 可以使用chcp获得windows的代码页(code page) C:\Documents and Settings\a105024\Desktop>chcp Active code page: 936 3. 创建测试表 SQL> create table test(id number,var varchar2(30)); Table created. 4. 插入数据 这里在同一个操作系统启动两个session,session1的NLS_LANG设为和数据库字符集一样(即AL32UTF8): C:\Documents and Settings\a105024\Desktop>set nls_lang=Simplified Chinese_China.AL32UTF8 连接数据库并插入一条数据: Session_1>insert into test values(1,'中国'); 1 row created. Session_1>commit; Commit complete. session2的NLS_LANG设为和客户端操作系统一样(即ZHS16GBK): C:\Documents and Settings\a105024\Desktop>set nls_lang=Simplified Chinese_China.ZHS16GBK 连接数据库并插入一条数据: Session_2>insert into test values(2,'中国'); 1 row created. Session_2>commit; Commit complete. 5. 执行查询 在session 1上执行查询: Session_1>select * from test; ID VAR ---------- --------------------- 1 中国 2 涓 浗 在session 2上执行查询: Session_2>select * from test; ID VAR ---------- -------------------- 1 ??? 2 中国
SQL> select id,dump(var,1016) from test; ID DUMP(VAR,1016) -- ------------------------------------------------------------ 1 Typ=1 Len=4 CharacterSet=AL32UTF8: d6,d0,b9,fa 2 Typ=1 Len=6 CharacterSet=AL32UTF8: e4,b8,ad,e5,9b,bd
但是根据上面实验我们可以知道,数据库中存储正确,并不代表客户端能正常显示;同样地,即时数据库没有正确存储,有时候客户端也能够正常显示,这又是为什么呢?别急,请听我慢慢道来:
场景1:session 1插入,session 1查询,在数据库中存储错误,但显示正确。
插入过程:
”中国“两字在客户端操作系统字符集ZHS16GBK中的编码是”d6,d0,b9,fa",由于NLS_LANG和数据库字符集相同,数据库端对客户端传过来的字符编码不进行任何转换直接存入数据库,因此数据库中存储的编码也是“d6,d0,b9,fa”,
读取过程:
数据库端读取的编码是“d6,d0,b9,fa”,由于NLS_LANG和数据库字符集相同,客户端对数据库端传过来的字符编码不进行任何转换直接显示,编码”d6,d0,b9,fa“在客户端操作系统字符集ZHS16GBK对应的汉字为“中国”。
从以上分析可知,虽然读取时正确的,但那是因为负负得正,实际上数据库中存储是错误的,因此要特别小心这种情况,在生成库中要避免。其实只要对它进行length操作就能轻易揭开它的假面具:
Session_1>select length(var) from test where id=1; LENGTH(VAR) ----------- 3
数据库端读取的编码是”e4,b8,ad,e5,9b,bd“,由于NLS_LANG和数据库字符集不同,客户端对数据库端传过来的字符编码进行转换,数据库端字符集AL32UTF8里”中国“两字的编码”e4,b8,ad,e5,9b,bd“转换成客户端操作系统字符集ZHS16GBK里“中国”两字的编码“d6,d0,b9,fa",并正常显示。
这种情况虽然经过了两次转换,都确实最正确、最推荐的方式。
结论:NLS_LANG只和客户端操作系统的字符集相关,如果客户端操作系统的字符集和数据库字符集间无法正确转换,则应该首先改变客户端终端的字符集,而不是简单地把NLS_LANG设为和数据库字符集一样。