要搞懂Oracle字符集,必须得先区分以下三个概念:
1. 数据库字符集,即数据库时以哪种字符编码存储字符的;
2. 客户端操作系统字符集
3. 客户端NLS_LANG参数
基本原理:1. 当NLS_LANG与数据库字符集相同时,数据库端对客户端传过来的字符编码不进行任何转换直接存入数据库。
2. 当NLS_LANG与数据库字符集不同时,数据库端对客户端传过来的字符编码会进行相应的转换。
为了更好的弄懂以上概念,请看以下
演示:
1. 查出数据库字符集
我们的数据库版本是10.2.0.4.0,数据库字符集是:
SQL> select * from v$nls_parameters where parameter='NLS_CHARACTERSET';
PARAMETER VALUE
---------------------------------------- ------------------------------
NLS_CHARACTERSET AL32UTF8
2. 查看操作系统字符集(code page)
可以使用chcp获得windows的代码页(code page)
C:\Documents and Settings\a105024\Desktop>chcp
Active code page: 936
通过查询微软的官方文档 (http://msdn.microsoft.com/en-gb/goglobal/bb896001.aspx),我们可以知道936是简体中文。
再通过metalink文档(https://support.oracle.com/CSP/main/article?cmd=show&type=NOT&id=179133.1),我们可以知道936对应的字符集为ZHS16GBK。
3. 创建测试表
SQL> create table test(id number,var varchar2(30));
Table created.
4. 插入数据
开两个session,第一个session的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.
第二个session的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 中国
分析:1. 到底哪个session插入的字符在数据库中是正确编码的呢?
我们可以使用dump函数去除字符在数据库中是怎样编码的:
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
根据AL32UTF8的编码,“中国”两字的正确编码为:
中--e4,b8,ad
国--e5,9b,bd
因此session 1插入的字符编码是错误的,session 2是正确的,下面再回顾一下刚刚做的实验,详细分析:
场景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
得出的长度居然为3!实际上的长度只是2。
场景2:session 1插入,session 2查询,在数据库中存储错误,显示也错误。
存储过程和场景1一样,这里就不在罗嗦了。
读取过程:
数据库端读取的编码是“d6,d0,b9,fa”,由于NLS_LANG和数据库字符集不同,客户端对数据库端传过来的字符编码进行转换,数据库端字符集AL32UTF8里编码为“d6,d0,b9,fa”无法在客户端操作系统字符集ZHS16GBK里找到对应的编码,于是用?代替。
场景3:session 2插入,session 1查询,在数据库中存储正确,但显示错误。
存储过程:
”中国“两字在客户端操作系统字符集ZHS16GBK中的编码是”d6,d0,b9,fa",由于NLS_LANG和数据库字符集不同,Oracle会进行字符编码转换,也就是将字符集ZHS16GBK里“中国”的编码“d6,d0,b9,fa"转换为字符集"AL32UTF8"里”中国“的编码”e4,b8,ad,e5,9b,bd“。
读取过程:
数据库端读取的编码是”e4,b8,ad,e5,9b,bd“,由于NLS_LANG和数据库字符集相同,客户端对数据库端传过来的字符编码不进行任何转换直接显示,编码”e4,b8,ad,e5,9b,bd“在客户端操作系统字符集ZHS16GBK对应的汉字为“涓 浗”。
场景4:session 2插入,session 2查询,在数据库中存储正确,显示也正确。
存储过程和场景3类似。
读取过程:
数据库端读取的编码是”e4,b8,ad,e5,9b,bd“,由于NLS_LANG和数据库字符集不同,客户端对数据库端传过来的字符编码进行转换,数据库端字符集AL32UTF8里”中国“两字的编码”e4,b8,ad,e5,9b,bd“转换成客户端操作系统字符集ZHS16GBK里“中国”两字的编码“d6,d0,b9,fa",并正常显示。
Oracle 字符集超集和子集的对应关系可查看:http://download.oracle.com/docs/cd/B19306_01/server.102/b14225/applocaledata.htm#sthref1988
NLS_LANG如何设置:NLS_LANG应该设为跟客户端操作系统字符集兼容。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/26277071/viewspace-709608/
,如需转载,请注明出处,否则将追究法律责任。