• 售前

  • 售后

热门帖子
入门百科

[Oracle] 彻底搞懂Oracle字符集

[复制链接]
静静的等258 显示全部楼层 发表于 2021-10-26 13:42:29 |阅读模式 打印 上一主题 下一主题
根本概念
字符集(Character set):
是一个体系支持的全部抽象字符的聚集。字符是各种笔墨和符号的总称,包罗各国家笔墨、标点符号、图形符号、数字等。常见的字符集有ASCII,ZHS16GB231280,ZHS16GBK等。

字符编码(Character Encoding):
是一套法则,使用该法则可以大概对自然语言的字符的一个聚集(如字母表或音节表),与别的的一个聚集(如电脑编码)进行配对。即在符号聚集与数字体系之间建立对应关系。与字符集相对应,常见的字符编码有:ASCii,ZHS16GBK,ZHT16BIG5,ZHS32GB18030等。
字符集的界说实在就是字符的聚集,而字符编码则是指怎么将这些字符变成字节用于生存、读取和传输。

万国码(Unicode):包含了几乎人类全部可用的字符,每年还在不停的增长,可以看作是一种通用的字符集。它将全天下全部的字符统一化,统一编码,不会再出现字符不兼容和字符转换的标题。
它有以下三种编码方式:
1.UTF-32编码:
固定使用4个字节来表示一个字符,存在空间使用服从的标题。
2.UTF-16编码:对相对常用的60000余个字符使用两个字节进行编码,其余的使用4字节。
3.UTF- 8编码:兼容ASCII编码;拉丁文、希腊文等使用两个字节;包罗汉字在内的别的常用字符使用三个字节;剩下的极少使用的字符使用四个字节。

Oracle字符集根本原理
在搞懂Oracle字符集根本原理之前,肯定要先分清以下三个概念:
1. Oracle数据库服务器字符集:即Oracle以哪种字符编码存储字符,可以通过以下语句查出数据库字符集的设置。
复制代码 代码如下:
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

根据该代码页,到微软的官方文档《National Language Support (NLS) API Reference》找到其对应的字符集。
如果是Linux,字符集在/etc/sysconfig/i18n设置:
复制代码 代码如下:
LANG="zh_CN.GB2312" (指定当前利用体系的字符集)
SUPPORTED="zh_CN.GB2312"(指定当前利用体系支持的字符集)
SYSFONT="lat0-sun16"(指定当前利用体系的字体)

3. 客户端NLS_LANG参数:该参数用于向Oracle指示客户端利用体系的字符集。
有了以上3个根本概念之后,我来阐述一下Oracle字符集转换的根本原则:
1.设置客户端的NLS_LANG为客户端利用体系的字符集
2.如果数据库字符集即是NLS_LANG,数据库和客户端传输字符时不作任何转换
3.如果它们俩不等,则必要在不同字符集间转换,只有客户端利用体系字符集是数据库字符集子集的基础上才能准确转换,否则会出现乱码。
几种常见环境分析
下面先看一个例子,再透过征象看本质,我们会针对这个例子进行分析。
该例子如下:
复制代码 代码如下:
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 中国

上面例子看起来很诡异,session1和2都能正常体现本身插入的字符串,又都不能正常体现对方插入的字符串。为了弄清楚,我们起首得知道数据库里对这两个字符串是怎么存储的。我们可以使用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的编码,“中国”两字的准确编码为(都为3个字节):
中--e4,b8,ad
国--e5,9b,bd
因此session 1插入的字符串在数据库中的编码是错误的,session 2准确。这也是为什么肯定要设置NLS_LANG为客户端利用体系的字符集。
但是根据上面实行我们可以知道,数据库中存储准确,并不代表客户端能正常体现;同样地,即时数据库没有准确存储,偶然候客户端也可以大概正常体现,这又是为什么呢?别急,请听我慢慢道来:
场景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对应的汉字为“涓   浗”(本来2个字符,现在变成了3个字符,由于ZHS16GBK的汉字以2个字节编码)。
场景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设为和数据库字符集一样。

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作