通过oracle类比MySQL中的字节字符问题

在几个月前写过一篇博文 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函数,但是我们可以通过类似相通的部分来互相印证。

时间: 2024-10-24 03:16:44

通过oracle类比MySQL中的字节字符问题的相关文章

oracle转Mysql中,varchar2(10)和number应该转换为什么类型? (转)

一. varchar2(10)和number应该转换为什么类型? oracle转成mysql时:varchar2(10)可以转成varchar(10)number则要看oracle中存储的具体是什么类型的数据:1.如果是整型,那么mysql中,用int即可:2.如果是带小数位的,那么mysql中可用numeric类型. 注:mysql中没有varchar2(10)和number这两个数据类型   二. Mysql varchar VS Oracle varchar2 mysql和oracle做数

关于Oracle和MySQL中的无密码登录

无密码登录在一定程度上能够简化流程,对于密码敏感,但是又需要提供访问权限的情况下是一个不错的选择.尤其是在乙方在做一些操作的时候,要密码和给密码是一个纠结的问题.不给没法工作,给了又对信息安全又影响. 在Oracle和MySQL中都有相应的解决方案,大道至简,这个功能的目的都是类似的. 在Oracle中可以通过设置wallet来实现,在10g版本开始支持.而在MySQL中自5.6版本开始可以使用--login-path来实现. 先来看看Oracle中的wallet实现无密码登录,可以通过mkst

SQL Server、Oracle和MySQL中查出值为NULL的替换

在SQL Server Oracle MySQL当数据库中查出某值为NULL怎么办? 1.MSSQL: ISNULL() 语法 ISNULL ( check_expression , replacement_value ) 参数 check_expression 将被检查是否为 NULL的表达式.check_expression 可以是任何类型的. replacement_value 在 check_expression 为 NULL时将返回的表达式.replacement_value 必须与

在C#和MySQL中存取中文字符时避免乱码的方法_Mysql

当用到socket来进行网络程序开发时,大多数情况下会遇到中文字符的发送与接收,这时若对发送的字符串用默认的方式进行处理,则一般会得到一堆乱码. 由于中文字符采用双字节表示,所以对含有中文的字符串的处理一定要按UNICODE编码方式进行处理,也就是说,使用socket发送中文字串时要事先将字串转成UNICODE格式的. 下面是简单的socket通信的代码. //服务端代码 try { IPAddress MyIP = IPAddress.Parse("127.0.0.1″); TcpListen

Java、JavaScript、Oracle、MySQL中实现的MD5加密算法分享_java

MD5,全称为 Message Digest Algorithm 5(消息摘要算法第五版).详情请参考 维基百科:MD5 MD5加密后是一个字节数组, 但我们一般是取其十六进制的字符串表示法,当然,十六进制数字符串是区分大小写,在 mysql数据库,Java,和JavaScript语言中,一般是使用小写的字符串来表示, 而在 Oracle数据库官方提供的包中,返回的是大写字符串,这算是一个坑,如果你想要执行多次 md5,可能需要转换为小写. 相关的代码如下: 1. Java版MD5 MD5Uti

Oracle和MySQL中短小精悍的SQL

如果让你写一个简单牛叉的SQL,数据库类型不限,你会写出什么样的SQL语句. Oracle    如果是Oracle,我就写个drop table dual; 这个SQL看起来很简单,包含的信息量还是蛮大的,首先对于dual表你得有一定的认识和了解,而这个视图和一般的数据字典不同,如果删除之后,直接会导致数据库不可用.恢复起来需要一个隐含参数来调整.    当然如果想换一个角度来,写出一些含有人生哲理的SQL来,这方面得下不少功夫了.    我想了一个,比如Flashback database

关于ORACLE和MYSQL中文字符乱码的根源剖析

关于数据库的字符集问题一直都是一个比较恶心的问题,如果不了解其实质可能一直 都搞不清楚这个问题的根源,只能出了问题去度娘,这里我打算兼容ORACLE和MYSQL对字符集 的处理来描述乱码出现的情形和如何防止乱码问题出现的可能,本文只用UTF-8和GBK为 例子进行描述,请大家先记住'去'这个字的UTF8和GBK编码,因为整个文章将用'去'字为 例子进行讲述 GBK     UTF8   中文 C8A5    E58EBB  去 同时整篇文章数据库DATABASE端的字符集始终为UTF8 一般来讲

Oracle和MySQL分组查询GROUP BY

Oracle和MySQL分组查询GROUP BY 真题1.Oracle和MySQL中的分组(GROUP BY)有什么区别?答案:Oracle对于GROUP BY是严格的,所有要SELECT出来的字段必须在GROUP BY后边出现,否则会报错:"ORA-00979: not a GROUP BY expression".而MySQL则不同,如果SELECT出来的字段在GROUP BY后面没有出现,那么会随机取出一个值,而这样查询出来的数据不准确,语义也不明确.所以,作者建议在写SQL语句

MySQL中如何实现类似Oracle的序列

Oracle一般使用序列(Sequence)来处理主键字段,而MySQL则提供了自增长(increment)来实现类似的目的: 但在实际使用过程中发现,MySQL的自增长有诸多的弊端:不能控制步长.开始索引.是否循环等:若需要迁移数据库,则对于主键这块,也是个头大的问题. 本文记录了一个模拟Oracle序列的方案,重点是想法,代码其次. Oracle序列的使用,无非是使用.nextval和.currval伪列,基本想法是:1.MySQL中新建表,用于存储序列名称和值:2.创建函数,用于获取序列表