关于char/varchar(n)中n的探究:字符数or字节数

问题来源

很多时候我们不确定某个字段的长度,会使用varchar类型,比如某个字段定义为varchar(100),那这100的长度能存多少个中文?

当时的想法就是:这个100能存多少个中文和数据库的编码方式有关,具体怎么个有关法还真是没有细细探究。网上的答案千奇百怪,很不给力,只能自己摸索了。

首先需要明确的是:在utf8编码方式下,一个中文占3个字节;而gbk编码下,一个中文占2个字节。

这个我们可以使用varchar类型存储一个字段vname,然后分别向其中添加一个汉字和英文。

select length(vname) from tablename;
select char_length(vname) from tablename,

你会发现二者的关系就是上述内容。

探索过程

回到那个问题,这100的长度到底能存多少个中文,既然一个中文占3个字节,是不是就只能存100/3=33个中文呢?换句话说,100这个数字是字节数,还是字符数?为此我做了如下测试:

在linux上的mysql中定义了下表

CREATE TABLE `test` (
  `v` varchar(4) default NULL,
  `c` char(4) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

即如下图

然后insert into test values ('围脖你好','围脖你好');,有2个warning,select之后才发现只存进去一个“新”字,后面的全丢了。这就说明,varchar(4)和char(4)中的4是字节数,即100是字节数,只能存33个中文字符。(备注:如果我们存储的字符串长度超过预定义的长度,mysql仍然可以存储成功,只是会提示warning,会将超出的部分自动截断。)

答案貌似明确了,但紧接着问题又来了!

因为以前听说,这个varchar(n)和char(n)中的n指的是存储的字符数,这不和我刚才做的测试完全不符吗?所以我又在本地windows下的mysql中做了同样的操作,建相同的表,插入相同的数据,看到结果,我傻眼了!!insert into test values ('围脖你好','围脖你好');,并未warning,而是query OK!select之后发现,这四个中文竟然都存进去了,这也就说明我们刚才定义的那个char(4)和varchar(4)中的4是字符数,而非字节数,这么说来,那100就是可以存100个中文字符了,这不完全将刚才的那个结论颠覆了??可是这两个结果都是对的啊,只是环境不同而已。

这样的话,那我们以后定义char和varchar字段还要先判断一下该环境下这个(n)到底代表的啥?是字节还是字符?

不甘心,想搞清楚原因,为何会出现这样的差别?

show variable like '%char%';

这下清晰了,linux下的和windows下的mysql相关变量编码定义完美呈现

实际上,character_set_client、character_set_connection、character_set_results是可以改变的,这个可以使用set names gbk/utf8, 只是为了和客户端编码页保持一致,不会乱码,如果客户端编辑页设定的文档是utf8,那么我们自然要set names utf8了,gbk同理。这个命令只会改变显示问题,底部的database的编码还是不会因为这个命令而受到影响的。包括server、system也不会随之改变,这时我们对比上下两张表,他们的server和system以及database的编码发现Linux下的都是Latin1,而windows下的这个都是utf8,

总结

我们知道Latin1都是存储字节为单位的字符比如数字、字母,一个Latin字符只占一个字节,而utf8存储的种类繁多,字符所占的字节数也就不确定了,utf8具有统一功能,其实网上大多是说char(n)存的是n个字符,就是因为此处的编码都是utf8的,utf8屏蔽了中文和英文和数字的显示区别,他们都是一个字符,所以定义的时候那个n就代表了字符的个数,具体占多少个字节是根据他们自身字符串的长度定的,比如char(100)存储100个中文,那么占300个字节,存100个字母,就占100个字节。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Spark学习技巧

Flink DataSet编程指南-demo演示及注意事项

Flink中的DataStream程序是对数据流进行转换的常规程序(例如,过滤,更新状态,定义窗口,聚合)。数据流的最初的源可以从各种来源(例如,消息队列,套接...

4.5K120
来自专栏PHP在线

正则表达式中的子组模式

作者:西瓜玩偶(racnil070512 at hotmail dot com) 一、基础知识 在PCRE正则表达式中,我们可以利用圆括号定义一个子组,我们...

411120
来自专栏加米谷大数据

Redis数据存储优化机制详解

将一个对象存储在hash类型中会占用更少的内存,并且可以更方便的存取整个对象。省内存的原因是新建一个hash对象时开始是用zipmap来存储的。这个zipmap...

12420
来自专栏对角另一面

lodash源码分析之数组的差集

本文为读 lodash 源码的第十七篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash

13940
来自专栏北京马哥教育

AWK处理日志入门

前言 这两天自己挽起袖子处理日志,终于把AWK给入门了。其实AWK的基本使用,学起来也就半天的时间,之前总是靠同事代劳,惰性呀。 此文仅为菜鸟入门,运维们请勿...

38740
来自专栏difcareer的技术笔记

JNI实现源码分析【四 函数调用】正文0x01:dvmCallMethodV0x02:nativeFunc0x03: 何时赋值

有了前面的铺垫,终于可以说说虚拟机是如何调用JNI方法的了。JNI方法,对应Java中的native方法,所以我们跟踪对Native方法的处理即可。

10840
来自专栏静晴轩

lua表排序

Lua作为一种很强大且轻量级脚本语言的存在,对于掌握其几乎无所不能的Table(其实就是一个Key Value的数据结构,它很像Javascript中的Obje...

493110
来自专栏用户2442861的专栏

初学Redis(3)——简单实现Redis缓存中的排序功能

http://blog.csdn.net/qtyl1988/article/details/39545531

7210
来自专栏zingpLiu

Linux用户信息查询

第一行显示当前时间,开机(up)多久,几个用户在系统上的平均负载等; 第二行是各个项目(列名)的说明; 第三行以后,每行代表一个用户。如上,root登录,并取得...

10430
来自专栏進无尽的文章

编码篇-低耦合代码注入

我下面要将的内容也许网上已经有很多相关的介绍了,但是我还是会写出这篇文章,一来是对自己学习的总结,虽然总结的有些晚,如果你仔细看,会发现我的文章有别处没有的内容...

11620

扫码关注云+社区

领取腾讯云代金券