☰
MySQL 数据类型详解:从数值到字符串,一篇讲透
2026/10/6 2:47:41 网站建设 项目流程

1. 前言

写 MySQL 的时候,建表是最基础也最容易翻车的一步。表建得好不好,很大程度上取决于你对数据类型的理解。类型选错了,轻则浪费空间,重则数据存不进去、查询出问题。

这篇博客把 MySQL 里常用的数据类型过一遍,包括数值、字符串、日期、枚举和集合,每个都配上实际执行的 SQL 和结果,方便你照着敲一遍。

2. 数值类型

数值类型是建表时用得最多的,先看分类,再逐个说细节。

2.1 tinyint 类型

tinyint 默认是有符号的,范围是 -128 到 127。如果你往里面塞一个超出范围的值,MySQL 会直接报错。

mysql>createtablett1(numtinyint);Query OK,0rowsaffected(0.02sec)mysql>insertintott1values(1);Query OK,1rowaffected(0.00sec)mysql>insertintott1values(128);-- 越界插入,报错ERROR1264(22003):Outofrangevalueforcolumn'num'atrow1mysql>select*fromtt1;+------+|num|+------+|1|+------+1rowinset(0.00sec)

MySQL 的整型默认是有符号的,如果你想让某个字段只能存非负数,可以加上unsigned关键字。

mysql>createtablett2(numtinyintunsigned);mysql>insertintott2values(-1);-- 无符号,范围是 0 - 255ERROR1264(22003):Outofrangevalueforcolumn'num'atrow1mysql>insertintott2values(255);Query OK,1rowaffected(0.02sec)mysql>select*fromtt2;+------+|num|+------+|255|+------+1rowinset(0.00sec)

这里有个建议:尽量别用 unsigned。比如 int 类型存不下的数据,int unsigned 同样可能存不下,与其这样,不如在建表时直接把 int 提升成 bigint,省得后面出问题。

2.2 bit 类型

bit 是位字段类型,语法是bit[(M)],M 表示每个值的位数,范围 1 到 64,不写的话默认是 1。

mysql>createtablett4(idint,abit(8));Query OK,0rowsaffected(0.01sec)mysql>insertintott4values(10,10);Query OK,1rowaffected(0.01sec)mysql>select*fromtt4;-- 发现很怪异的现象,a的数据10没有出现+------+------+|id|a|+------+------+|10||+------+------+1rowinset(0.00sec)

这里有个坑:bit 字段在显示的时候,是按照 ASCII 码对应的值显示的。所以存 10 显示不出来,存 65 会显示成A。

mysql>insertintott4values(65,65);mysql>select*fromtt4;+------+------+|id|a|+------+------+|10|||65|A|+------+------+

如果你只需要存 0 或 1,可以定义成bit(1),这样能省空间。

mysql>createtablett5(genderbit(1));mysql>insertintott5values(0);Query OK,1rowaffected(0.00sec)mysql>insertintott5values(1);Query OK,1rowaffected(0.00sec)mysql>insertintott5values(2);-- 当插入2时,已经越界了ERROR1406(22001):Datatoo longforcolumn'gender'atrow1

2.3 小数类型

小数类型主要有 float 和 decimal 两种,各有各的适用场景。

2.3.1 float

float 的语法是float[(m, d)] [unsigned],M 指定显示长度,d 指定小数位数,占用 4 个字节。

mysql>createtablett6(idint,salaryfloat(4,2));Query OK,0rowsaffected(0.01sec)mysql>insertintott6values(100,-99.99);Query OK,1rowaffected(0.00sec)mysql>insertintott6values(101,-99.991);-- 多的这一点被拿掉了Query OK,1rowaffected(0.00sec)mysql>select*fromtt6;+------+--------+|id|salary|+------+--------+|100|-99.99||101|-99.99|+------+--------+2rowsinset(0.00sec)

float(4,2)表示的范围是 -99.99 到 99.99,MySQL 在保存值的时候会做四舍五入。如果指定了 unsigned,范围就变成 0 到 99.99。

mysql>createtablett7(idint,salaryfloat(4,2)unsigned);mysql>insertintott7values(100,-0.1);Query OK,1rowaffected,1warning(0.00sec)mysql>showwarnings;+---------+------+-------------------------------------------------+|Level|Code|Message|+---------+------+-------------------------------------------------+|Warning|1264|Outofrangevalueforcolumn'salary'atrow1|+---------+------+-------------------------------------------------+1rowinset(0.00sec)mysql>insertintott7values(100,-0);Query OK,1rowaffected(0.00sec)mysql>insertintott7values(100,99.99);Query OK,1rowaffected(0.00sec)
2.3.2 decimal

decimal 是定点数,语法是decimal(m, d),m 指定长度,d 表示小数位数。decimal(5,2)表示范围是 -999.99 到 999.99,unsigned 的话是 0 到 999.99。

float 和 decimal 最大的区别是精度。float 的精度大约是 7 位,decimal 的整数最大位数 m 是 65,小数最大位数 d 是 30。如果希望精度高,推荐用 decimal。

mysql>createtablett8(idint,salaryfloat(10,8),salary2decimal(10,8));mysql>insertintott8values(100,23.12345612,23.12345612);Query OK,1rowaffected(0.00sec)mysql>select*fromtt8;+------+-------------+-------------+|id|salary|salary2|+------+-------------+-------------+|100|23.12345695|23.12345612|-- 发现decimal的精度更准确+------+-------------+-------------+

同样的值,float 存出来变成了 23.12345695,decimal 还是 23.12345612。所以对精度有要求的场景,比如金额,直接用 decimal。

3. 字符串类型

字符串类型主要就是 char 和 varchar,这俩是面试常客,也是建表时最容易纠结的。

3.1 char

char 是固定长度字符串,语法是char(L),L 是存储的长度,单位是字符,最大 255。

mysql>createtablett9(idint,namechar(2));Query OK,0rowsaffected(0.00sec)mysql>insertintott9values(100,'ab');Query OK,1rowaffected(0.00sec)mysql>insertintott9values(101,'中国');Query OK,1rowaffected(0.00sec)mysql>select*fromtt9;+------+--------+|id|name|+------+--------+|100|ab||101|中国|+------+--------+

char(2) 可以放两个字符,字母或汉字都行,但超过 255 就会报错。

mysql>createtablett10(idint,namechar(256));ERROR1074(42000):Columnlength too bigforcolumn'name'(max=255);useBLOBorTEXTinstead

3.2 varchar

varchar 是可变长度字符串,语法是varchar(L),L 表示字符长度,最大 65535 个字节。

mysql>createtablett10(idint,namevarchar(6));-- 表示这里可以存放6个字符mysql>insertintott10values(100,'hello');mysql>insertintott10values(100,'我爱你,中国');mysql>select*fromtt10;+------+--------------------+|id|name|+------+--------------------+|100|hello||100|我爱你,中国|+------+--------------------+

关于 varchar(len) 到底能设多大,这个和表的编码密切相关。varchar 长度可以指定 0 到 65535 之间的值,但有 1 到 3 个字节用于记录数据大小,所以有效字节数是 65532。

  • 表编码是 utf8 时,一个字符占 3 个字节,varchar(n) 的 n 最大值是 65532/3 = 21844
  • 表编码是 gbk 时,一个字符占 2 个字节,varchar(n) 的 n 最大值是 65532/2 = 32766

验证一下:

mysql>createtablett11(namevarchar(21845))charset=utf8;-- 验证了utf8确实是不能超过21844ERROR1118(42000):Rowsize too large.The maximumrowsizeforthe usedtabletype,notcounting BLOBs,is65535.You havetochangesomecolumnstoTEXTorBLOBs mysql>createtablett11(namevarchar(21844))charset=utf8;Query OK,0rowsaffected(0.01sec)

3.3 char 和 varchar 怎么选

这个选择其实不难,记住几条原则就行:

  • 如果数据长度确定都一样,用定长 char,比如身份证、手机号、md5
  • 如果数据长度有变化,用变长 varchar,比如名字、地址,但要保证最长的能存进去
  • 定长的磁盘空间比较浪费,但效率高
  • 变长的磁盘空间比较节省,但效率低

定长的意义是直接开辟好对应的空间,变长的意义是在不超过自定义范围的情况下,用多少开辟多少。

4. 日期和时间类型

常用的日期类型有三个:

  • date:日期,格式yyyy-mm-dd,占用 3 字节
  • datetime:时间日期,格式yyyy-mm-dd HH:ii:ss,范围从 1000 到 9999,占用 8 字节
  • timestamp:时间戳,从 1970 年开始,格式和 datetime 完全一致,占用 4 字节
-- 创建表mysql>createtablebirthday(t1date,t2datetime,t3timestamp);Query OK,0rowsaffected(0.01sec)-- 插入数据mysql>insertintobirthday(t1,t2)values('1997-7-1','2008-8-8 12:1:1');Query OK,1rowaffected(0.00sec)mysql>select*frombirthday;+------------+---------------------+---------------------+|t1|t2|t3|+------------+---------------------+---------------------+|1997-07-01|2008-08-0812:01:01|2017-11-1218:28:55|-- 添加数据时,时间戳自动补上当前时间+------------+---------------------+---------------------+-- 更新数据mysql>updatebirthdaysett1='2000-1-1';Query OK,1rowaffected(0.00sec)mysql>select*frombirthday;+------------+---------------------+---------------------+|t1|t2|t3|+------------+---------------------+---------------------+|2000-01-01|2008-08-0812:01:01|2017-11-1218:32:09|-- 更新数据,时间戳会更新成当前时间+------------+---------------------+---------------------+

注意 timestamp 的特性:插入数据时自动补上当前时间,更新数据时也会自动更新成当前时间。这个特性在很多场景下很实用。

5. enum 和 set

这俩是 MySQL 里比较特殊的类型,一个是单选,一个是多选。

5.1 enum 枚举

enum 是"单选"类型,语法是enum('选项1','选项2','选项3',...)。它只提供若干个选项,最终一个单元格只存其中一个值。出于效率考虑,这些值实际存储的是数字,每个选项依次对应 1,2,3…,最多 65535 个。添加枚举值时也可以直接加数字编号。

5.2 set 集合

set 是"多选"类型,语法是set('选项值1','选项值2','选项值3',...)。一个单元格可以存其中任意多个值。同样出于效率考虑,这些值实际存储的是数字,每个选项依次对应 1,2,4,8,16,32…,最多 64 个。

这里有个建议:添加枚举值、集合值的时候别用数字方式,不利于阅读。

5.3 实际案例

建一个调查表 votes,调查人的喜好,比如登山、游泳、篮球、武术(可多选),性别(单选)。

mysql>createtablevotes(->usernamevarchar(30),->hobbyset('登山','游泳','篮球','武术'),-- 使用数字标识每个爱好,想想Linux权限,采用比特位位置->genderenum('男','女'));-- 使用数字标识的时候,就是正常的数组下标Query OK,0rowsaffected(0.02sec)insertintovotesvalues('雷锋','登山,武术','男');insertintovotesvalues('Juse','登山,武术',2);select*fromvoteswheregender=2;+----------+---------------+--------+|username|hobby|gender|+----------+---------------+--------+|Juse|登山,武术|女|+----------+---------------+--------+

现在想查找所有喜欢登山的人,如果直接用等值查询:

mysql>select*fromvoteswherehobby='登山';+----------+--------+--------+|username|hobby|gender|+----------+--------+--------+|LiLei|登山|男|+----------+--------+--------+

这样查不出来所有爱好为登山的人,因为有的人爱好是"登山,武术"。这时候要用find_in_set函数。

find_in_set(sub, str_list):如果 sub 在 str_list 中,返回下标;如果不在,返回 0。str_list 是用逗号分隔的字符串。

mysql>selectfind_in_set('a','a,b,c');+---------------------------+|find_in_set('a','a,b,c')|+---------------------------+|1|+---------------------------+mysql>selectfind_in_set('d','a,b,c');+---------------------------+|find_in_set('d','a,b,c')|+---------------------------+|0|+---------------------------+mysql>select*fromvoteswherefind_in_set('登山',hobby);+----------+---------------+--------+|username|hobby|gender|+----------+---------------+--------+|雷锋|登山,武术|男||Juse|登山,武术|女||LiLei|登山|男|+----------+---------------+--------+

这样就能把所有喜欢登山的人都查出来了。

6. 小结

数据类型这块,记住几个关键点就够了:

  • 整型默认有符号,unsigned 能不用就不用,存不下就升级 bigint
  • bit 类型显示按 ASCII 码,只存 0/1 用 bit(1)
  • 小数精度要求高用 decimal,别用 float
  • 定长用 char,变长用 varchar,注意编码对 varchar 长度的影响
  • timestamp 会自动更新,适合记录操作时间
  • enum 单选、set 多选,查询 set 用 find_in_set

建表前先想清楚每个字段的类型,后面能省不少事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询