ORACLE字符集解析(单引号失踪之谜)

为什么我们用ORACLE处理中文总不能如意,为什么我们在直接下SQL处理中文,会造成字符丢失, 著名的"单引号失踪"案到底是怎么回事?

本文将一一为您解答:

[@more@]

篇首鸣谢:牛人Willie(山歌)

前段时间研究ORACLE对数据的存储的时候, 曾经有了解过一些关于ORACLE字符集的东西, 但是苦于有个问题一只萦绕于心, 所以没有什么东西产出。

这个问题就是我们客户端与服务器端的字符集都设成UTF8的时候, 当我们在SQL Navigator中直接下SQL操作某些中文字符串的时候, 会莫名奇妙的出现错误,例如

SELECT 济南FROM DUAL

就会出错, 提示说少了一个单引号:

0:14:32 ORA-01756: quoted string not properly terminated

但是我们用下面这种不符合语法的SQL,却可以:

SELECT 济南’’ FROM DUAL

虽然有知道,是因为客户端字符集与服务器端字符集一致, 所以将上述SQL传入ORACLE的时候, 系统不会为其进行字符集转换,而ORACLE在将传入的ZHS16GBK编码以UTF8的方式来读取的时候,发生错误,将单引号给吞掉了。

但是, 知其然而不知其所以然, 总是觉得很难受, 找了很多资料, 都没有得到正解。都快积郁成疾了。 最终, Team内牛人出山了, 用一个哥德巴赫猜想, 一解俺的困惑。

我们知道UTF8在存储的时候, 是以如下方式进行编码的:

当要表示的内容是 7位 的时候就用一个字节:0*******  

第一个0为标志位,剩下的空间正好可以表示ASCII 0127 的内容。

当要表示的内容在 8 到 11 位的时候就用两个字节:110***** 10******  

第一个字节的110和第二个字节的10为标志位。

当要表示的内容在 12 到 16 位的时候就用三个字节:1110***** 10****** 10******    和上面一样,第一个字节的1110和第二、三个字节的10都是标志位,剩下的空间正好可以表示汉字, 因为我们的汉字就是用2个字节16位来存储的。以此类推:

四个字节:11110**** 10****** 10****** 10****** 五个字节:111110*** 10****** 10****** 10****** 10****** 六个字节:1111110** 10****** 10****** 10****** 10****** 10******

不过目前ORACLE 9I中常看到的UTF8字符集之支持最大3个字节的UTF8编码, 所以严格来说,ORACLE 9I中的UTF8并没有完全实现UTF8 大概是因为目前3个字节就已经够用了, 完全可以用来存储当今已经存在的各种标准字符集。

我们的猜想如下:服务器端字符集是UTF8的时候, 基于以上规则, ORACLE引擎会以如下的方式来解析传入的字节流:

每次一个字节读入,

check到第一位为”0”,则以ASCII码方式解析这一个字节。

2位为”10”, 非法,忽略当前字节。因为按照上面的规则, 不可能有以”10”开头的。

3位为”110”, 读入2个字节。

3位为”1110”, 读入3个字节。

4位为”1111”, 非法, 忽略当前字节。因为9IUTF8不支持这种。

所有当其解析济南的时候, 会以如下方式解析:

我们知道单引号的ASCII码是27, “济南GBK编码是 BCC3 C4CF, 所以以上字符串的字节流便是:

00011011 10111100 11000011 11000100 11001111 00011011

第一个字节, ”0”开头,解析为”’”

第二个字节, ”10”开头, 忽略

第三个字节, ”110”开头,读入2个字节

第五个字节, ”110”开头,读入2个字节

注意,它将最后一个单引号当作与前面一个字节一起当作一个字符读入了,所以其SQL引擎找不到最后那个单引号了, 所以认为语法有错误。

其它各种中文出错的情况也都可模拟出来。至于如何解决, 就看各位自己的了, 知道了缘由, 办法自然就有了。

特别鸣谢: 文中的牛人Willie(山歌)

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

转载于:http://blog.itpub.net/767125/viewspace-969030/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值