通过oracle类比MySQL中的字节字符问题(r4笔记第44天)

时间:2022-05-04
本文章向大家介绍通过oracle类比MySQL中的字节字符问题(r4笔记第44天),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

在几个月前写过一篇博文 MySQL数据类型 http://blog.itpub.net/23718752/viewspace-1371434/ 当时写完以后有同事朋友就提出了一些疑问,对于汉字在MySQL和Oracle中的存放情况希望我能够详细的说说。 关于MySQL中的varchar字符类型,自己的操作都是基于字符集UTF-8。 对于存放汉字,涉及到字符,字节,编码的一些知识,我查了一下,自己先补补,发现有一个帖子已经描述的很详细了。直接引用过来。 http://www.regexlab.com/zh/encoding.htm 从编码的发展来看,大致可以分为三个阶段。系统内码 说明 系统 阶段一 ASCII 计算机刚开始只支持英语,其它语言不能够在计算机上存储和显示。 阶段二 ASCII(本地化) 为使计算机支持更多语言,通常使用 0x80~0xFF 范围的 2 个字节来表示 1 个字符。比如:汉字 '中' 在中文操作系统中,使用 [0xD6,0xD0] 这两个字节存储。 然后不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。 阶段三 UNICODE(国际化) 为了使国际间信息交流更加方便,国际组织制定了 UNICODE 字符集,为各种语言中的每一个字符设定了统一并且唯一的数字编号,以满足跨语言、跨平台进行文本转换、处理的要求。 因为对Oracle中的一些细节略为熟悉,所以能够旁敲侧击出MySQL中的一些相通的地方。 在编码的基础上,字符,字节的关系就很重要了。 字符是一个抽象意义的符号,一个汉字或一个字母都是一个字符。 而字节是计算机中存储数据的单元,一个8位的二进制数 如果对Oracle接触长了,再用MySQL,一个很纠结的地方就是汉字的存放,在MySQL中,汉字和字母都是平等对待的,都是按照字符来存放的。 但是Oracle中却不然,可以聚个简单的例子。 SQL> create table test (name varchar2(6)); Table created. SQL> insert into test values('123456'); 1 row created. SQL> insert into test values('一二三'); insert into test values('一二三') * ERROR at line 1: ORA-12899: value too large for column "SYS"."TEST"."NAME" (actual: 9, maximum:6) SQL> insert into test values('一二'); 1 row created. 在Oracle中有一个dump函数能够很清晰的查看出数据的存储情况。 比如下面的情况,查看汉字和字母,每个汉字是按照3个字节来存放的,每个字母则是一个字节。 SQL> select dump('你好') from dual; DUMP('你好') ------------------------------------- Typ=96 Len=6: 228,189,160,229,165,189 SQL> select dump('a') from dual; DUMP('A') ---------------- Typ=96 Len=1: 97 这一点和MySQL存在着明显的差别,Oracle中其实也可以得到和MySQL同样的效果。 这就涉及到一个数据库参数NLS_LENGTH_SEMANTICS,这个参数用于指定CHAR列或VARCHAR2列的长度定义方式,默认值为BYTE。当设置该参数为BYTE时,定义CHAR列或VARCHAR2列采用字节长度方式;当设置该参数为CHAR时,定义CHAR列或VARCHAR2列采用字符个数方式。 为了不伤筋动骨,我就在session级别做一些变更来说明这个问题。首先把它从byte变更为char(注意这个参数在oracle中是作为基本的初始化参数,一般不需要修改) SQL>ALTER SESSION SET nls_length_semantics=char; 创建一个测试表,指定字段长度为varchar2(6) SQL> create table test_char (name varchar2(6)); Table created. SQL> insert into test_char values('北京'); 1 row created. SQL> insert into test_char values('北京欢迎你'); --插入5个字符也没有问题,情况和之前明显不同。 1 row created. SQL> insert into test_char values('北京欢迎你啊'); --插入6个字符也没有问题。 1 row created. SQL> insert into test_char values('北京欢迎你哈哈'); insert into test_char values('北京欢迎你哈') * ERROR at line 1: ORA-12899: value too large for column "N1"."TEST_CHAR"."NAME" (actual: 8, maximum: 6) 然后我们为了对别插入两组数字看看效果。

SQL> insert into test_char values('1234');
1 row created.
SQL> insert into test_char values(123456);
1 row created.
SQL> select name,dump(name) text from test_char;
NAME                     TEXT
------------------------ ----------------------------------------------------------------------------------------------------
北京                     Typ=1 Len=6: 229,140,151,228,186,172
北京欢迎你               Typ=1 Len=15: 229,140,151,228,186,172,230,172,162,232,191,142,228,189,160
北京欢迎你啊             Typ=1 Len=18: 229,140,151,228,186,172,230,172,162,232,191,142,228,189,160,229,149,138
1234                     Typ=1 Len=4: 49,50,51,52
123456                   Typ=1 Len=6: 49,50,51,52,53,54

这样来查看好像和最开始的情况没有任何的变化。可以看到还是按照三个字节来存放,但是代表的意义已经发生了变化。 在MySQL中的情况和在oracle 参数nls_length_semantics=char;的情况是类似的。


SQL>create table test(name varchar(10));
SQL>insert into test values('1234567890');
Query OK, 1 row affected (0.00 sec)
SQL>insert into test values('一二三四五六七八九十');
Query OK, 1 row affected (0.00 sec)
SQL>insert into test values(null,12345678901);
ERROR 1406 (22001): Data too long for column 'name' at row 1
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> show warnings;
+---------+------+-------------------------------------------+
| Level   | Code | Message                                   |
+---------+------+-------------------------------------------+
| Warning | 1265 | Data truncated for column 'name' at row 1 |    --最后这个地方直接给truncate掉了多余的部分。
+---------+------+-------------------------------------------+
1 row in set (0.00 sec)

这一点在数据迁移的时候如果不注意就是很严重的问题。不知道MySQL中是否也有和oracle中类似的dump函数,但是我们可以通过类似相通的部分来互相印证。