前言:本文详细介绍了MySQL中的数据类型分类,包括数值类型、浮点类、字符串类、日期和时间类型以及enum和set类型。解释了其存储范围及使用场景,并展示了相关测试代码。通过这些数据类型的合理选择,可以优化数据库的存储和查询性能。希望本文能为您在MySQL数据类型选择上提供有价值的参考。
目录
数据类型分类
一、数值类型

这里以tinyint为例进行讲解,其他类型原理相同,只是占用的字节空间不同。
注:MySQL语法是不区分大小写的,操作时使用小写更具可读性。
1.tinyint
tinyint存储的整数类型,大小为1个字节,所以有符号的tinyint的存储范围是[-128,127]。
测试代码:
sql
mysql> create table t1(num tinyint);
Query OK, 0 rows affected (0.02 sec)
mysql> insert into t1 values(-128);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1 values(127);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1 values(128);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> select*from t1;
+------+
| num |
+------+
| -128 |
| 127 |
+------+
2 rows in set (0.00 sec)
mysql>
我们可以发现在插入超过存储范围的值时是无法插入的,这与C/C++不同,C/C++会直接执行截断机制。而MySQL直接报错。这也反向说明插入MySQL的数据一定是合法的。
MySQL的数据类型本身就是一种约束,它促使程序员正确填写数据。
tinyint unsigned存储范围为[0,255],测试:
sql
mysql> create table t2(num tinyint unsigned);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t2 values(-1);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> insert into t2 values(0);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t2 values(255);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t2 values(256);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> select*from t2;
+------+
| num |
+------+
| 0 |
| 255 |
+------+
2 rows in set (0.00 sec)
mysql>
smallint、mediumint、int、bigint和tinyint原理都一样,只是占用字节空间不同而已,在开发过程中按所需空间大小选择就行。
2.bit
bit类型的存储单位是比特位。
- 语法:bit[(M)] : 位字段类型。M表示每个值的位数(即需要几个比特位),范围从1到64。如果M被忽略,默认为1。
测试代码:
sql
mysql> create table t3(
-> code int,
-> online bit(1)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t3 (code,online) values (20250403,1);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t3 (code,online) values (20250404,0);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t3 (code,online) values (20250404,3);
ERROR 1406 (22001): Data too long for column 'online' at row 1
mysql> insert into t3 (code,online) values (20250404,-1);
ERROR 1406 (22001): Data too long for column 'online' at row 1
mysql>
如上表示用户是否在线,是两态的,为节省空间只需要使用1个比特位来表示,又或者是性别等。1个比特的范围是[0,1],在范围外无法插入。
需要使用位图表示数据时可以选用该类型。
二、浮点类
1.float
语法:float[(m, d)] [unsigned] : m指定显示长度,d指定小数位数,占用空间4个字节。
m表示总长度,,d表示小数位数长度,所以剩下整数位数长度不能超过m-d。
sql
mysql> create table t4(num float(4,2));
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t4 values(25.46);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t4 values(5.6);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t4 values(99.99);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t4 values(-99.99);
Query OK, 1 row affected (0.00 sec)
mysql>
根据float的存储规则可知,float(4,2)的存储范围为[-99.99,99.99]。
在做如下测试:
sql
mysql> insert into t4 values(100.0);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> insert into t4 values(45.45645);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t4 values(45.45145);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t4 values(99.995);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> insert into t4 values(99.994);
Query OK, 1 row affected (0.01 sec)
mysql>
如上,100它的整数位已经超过2了,不在[-99.99,99.99]范围,无法插入,而45.45645,45.45145它都超出了指定的小数位数(2位),但依旧能插入,事实上MySQL只保留了两位小数,它进行了四舍五入。
99.995四舍五入后为100.0所以无法插入,而99.994四舍五入后为99.99所以能插入
sql
mysql> select*from t4;
+--------+
| num |
+--------+
| 25.46 |
| 5.60 |
| 99.99 |
| -99.99 |
| 45.46 |
| 45.45 |
| 99.99 |
+--------+
7 rows in set (0.00 sec)
mysql>
无符号float测试:
sql
mysql> create table t6 (num float(4,2) unsigned);
Query OK, 0 rows affected (0.02 sec)
mysql> insert into t6 values(-1);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> insert into t6 values(0);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t6 values(99.99);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t6 values(100.0);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql>
相比有符号的float,无符号float就是简单粗暴地把[-99.99,-1]这个范围给砍掉。即存储范围为[0,99.99],其它规则都一样。
2.decimal
double,decimal的使用方法都是一样只是精度不同,decimal的精度更高,float的精度大约为7位,decimal的精度:整数位65,小数位30,这和MySQL版本有关。
测试:
sql
mysql> create table t7(
-> fnum float(10,8),
-> dnum decimal(10,8)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t7 values(32.123456789,32.123456789);
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> select*from t7;
+-------------+-------------+
| fnum | dnum |
+-------------+-------------+
| 32.12345505 | 32.12345679 |
+-------------+-------------+
1 row in set (0.00 sec)
用两个类型存相同的数,float有精度损失,而decimal能准确存储。
三、字符串类
字符串类主要有char和varchar。
- char(L): 固定长度字符串,L是可以存储的长度,单位为字符,最大可以为255字符。
- varchar(L): 可变长度字符串,L表示字符长度,最大长度65535个字节。
sql
mysql> create table t8(ch char(2));
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t8 values('a');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t8 values('ab');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t8 values('abc');
ERROR 1406 (22001): Data too long for column 'ch' at row 1
mysql> insert into t8 values('你好');
Query OK, 1 row affected (0.01 sec)
mysql> insert into t8 values('你好呀');
ERROR 1406 (22001): Data too long for column 'ch' at row 1
mysql> select*from t8;
+--------+
| ch |
+--------+
| a |
| ab |
| 你好 |
+--------+
3 rows in set (0.00 sec)
mysql>
注意:char(2)中2表示最大能储存的字符个数,而不是字节数。即使两个中文字符在utf8中占6字节,但它依旧是两字符。
sql
mysql> create table t10(ss varchar(65535));
ERROR 1074 (42000): Column length too big for column 'ss' (max = 21845); use BLOB or TEXT instead
mysql> create table t10(ss varchar(21845));
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs
mysql> create table t10(ss varchar(21844));
Query OK, 0 rows affected (0.01 sec)
mysql>
如上我们创建65535,但无法创建,提示最大为21845,是因为varchar(L)中L表示的是字符数,最大能存储65535个字节,中文字符占3字节,65535*3=21845。但我们填入21845依旧无法创建,因为varchar是变长字符串,还需要拿3字节来存储字符的长度。所以最大能存储的字符为65532*3=21844。
varchar的使用方法和char相同。如果需要存储大文本可以使用text类型。
存储方式
CHAR:固定长度字符串,分配的空间是固定的。
VARCHAR:可变长度字符串,只占用实际需要的空间加上长度标识符。
存储空间
CHAR:无论实际存储内容长度如何,都会占用定义时指定的空间
- 例如CHAR(10)总是占用10个字符的空间
VARCHAR:只占用实际内容长度+1或2个字节(用于存储长度信息)
- 例如VARCHAR(10)存储"abc"只占用4个字节(3+1)
性能比较
CHAR:
读取速度通常更快(固定长度,无需计算)
适合存储长度变化不大的数据(如MD5哈希值、固定编码等)
VARCHAR:
节省存储空间
适合存储长度变化较大的数据
填充方式
CHAR:不足长度会用空格填充到指定长度。
VARCHAR:不会填充,按实际内容存储。
最大长度
CHAR:最大255个字符
VARCHAR:
MySQL 5.0.3之前最大255字节
MySQL 5.0.3及之后最大65,535字节(实际约65,532字符,因其他开销)
使用场景
使用CHAR:
存储固定长度的数据(如国家代码、性别标志等)
需要频繁更新的列(减少碎片)
使用VARCHAR:
存储长度变化大的数据(如用户名、地址等)
对存储空间敏感的应用
四、日期和时间类型
表示时间的类型有date、datetime、timestamp
- date:日期 'yyyy-mm-dd' ,占用3字节
- datetime:时间日期格式 'yyyy-mm-dd HH:ii:ss' 表示范围从 1000 到 9999 ,占用8字节
- timestamp:时间戳,从1970年开始的 yyyy-mm-dd HH:ii:ss 格式和 datetime 完全一致,占用4字节。
注意timestamp类型不需要我们填写,在我们修改表时系统会自动更新。
测试:
sql
mysql> create table t11(
-> birthday date,
-> start datetime,
-> front timestamp
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t11 (birthday,start) values('2004-7-19','2023-9-1 8:30:25');
Query OK, 1 row affected (0.00 sec)
mysql> select*from t11;
+------------+---------------------+---------------------+
| birthday | start | front |
+------------+---------------------+---------------------+
| 2004-07-19 | 2023-09-01 08:30:25 | 2025-05-11 15:06:14 |
+------------+---------------------+---------------------+
1 row in set (0.00 sec)
mysql>
五、enum和set
enum是一种字符串对象,用于存储预定义的一组值中的单个值。
-
在建表时定义一组允许的值(枚举列表)。
-
插入数据时,该列只能选择枚举列表中的一个值。
-
适合存储单选数据,如性别、状态等。
set也是一种字符串对象,用于存储预定义的一组值中的零个或多个值。
-
在建表时定义一组允许的值(集合列表)。
-
插入数据时,该列可以选择集合列表中的多个值(用逗号分隔)。
-
适合存储多选数据,如用户兴趣、标签等。
测试:
sql
mysql> create table t12(
-> name char(10),
-> gender enum('男','女'),
-> hobby set('敲代码','篮球','羽毛球','滑雪','围棋')
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t12 values('张三','男','敲代码');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t12 values('李四','女','敲代码,滑雪');
Query OK, 1 row affected (0.01 sec)
mysql> insert into t12 values('李四','女','敲代码,滑雪,围棋');
Query OK, 1 row affected (0.00 sec)
mysql> select*from t12;
+--------+--------+-------------------------+
| name | gender | hobby |
+--------+--------+-------------------------+
| 张三 | 男 | 敲代码 |
| 李四 | 女 | 敲代码,滑雪 |
| 李四 | 女 | 敲代码,滑雪,围棋 |
+--------+--------+-------------------------+
3 rows in set (0.00 sec)
mysql>
如上,对于enum类型的元素,男和女本质就是1和2。 可以直接填[1,2]超出这个范围无法插入。
sql
mysql> insert into t12 values('王五',1,'羽毛球');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t12 values('王五',2,'篮球');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t12 values('王五',0,'篮球');
ERROR 1265 (01000): Data truncated for column 'gender' at row 1
mysql> insert into t12 values('王五',3,'篮球');
ERROR 1265 (01000): Data truncated for column 'gender' at row 1
mysql>
对于set类型也类似不过它使用的是位图的思想。
如上set中有5种元素,那么需要5个比特位为00000,从右往左分别代表: '敲代码','篮球','羽毛球','滑雪','围棋'。但插入时是以十进制插入的,比如选择篮球和滑雪,也就是:01010即10。如下:
sql
mysql> insert into t12 values('赵六',1,1);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t12 values('张三',2,10);
Query OK, 1 row affected (0.00 sec)
mysql> select*from t12;
+--------+--------+-------------------------+
| name | gender | hobby |
+--------+--------+-------------------------+
| 张三 | 男 | 敲代码 |
| 李四 | 女 | 敲代码,滑雪 |
| 李四 | 女 | 敲代码,滑雪,围棋 |
| 王五 | 男 | 羽毛球 |
| 王五 | 女 | 篮球 |
| 赵六 | 男 | 敲代码 |
| 张三 | 女 | 篮球,滑雪 |
+--------+--------+-------------------------+
7 rows in set (0.00 sec)
mysql>
enum和set的查找,如下:
sql
mysql> select*from t12 where gender='男';
+--------+--------+-----------+
| name | gender | hobby |
+--------+--------+-----------+
| 张三 | 男 | 敲代码 |
| 王五 | 男 | 羽毛球 |
| 赵六 | 男 | 敲代码 |
+--------+--------+-----------+
3 rows in set (0.00 sec)
mysql> select*from t12 where hobby='敲代码';
+--------+--------+-----------+
| name | gender | hobby |
+--------+--------+-----------+
| 张三 | 男 | 敲代码 |
| 赵六 | 男 | 敲代码 |
+--------+--------+-----------+
2 rows in set (0.00 sec)
mysql>
我们发现用这种方式找set只能找到只有某个元素的那一栏。但我们需要查的是有某个元素的那一栏, 怎么办呢?
可以使用find_in_set函数,如下:
sql
mysql> select*from t12 where find_in_set('滑雪',hobby);
+--------+--------+-------------------------+
| name | gender | hobby |
+--------+--------+-------------------------+
| 李四 | 女 | 敲代码,滑雪 |
| 李四 | 女 | 敲代码,滑雪,围棋 |
| 张三 | 女 | 篮球,滑雪 |
+--------+--------+-------------------------+
3 rows in set (0.00 sec)
mysql> select*from t12 where find_in_set('敲代码',hobby) and find_in_set('滑雪',hobby);
+--------+--------+-------------------------+
| name | gender | hobby |
+--------+--------+-------------------------+
| 李四 | 女 | 敲代码,滑雪 |
| 李四 | 女 | 敲代码,滑雪,围棋 |
+--------+--------+-------------------------+
2 rows in set (0.01 sec)
mysql>