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


这里以tinyint为例进行讲解,其他类型原理相同,只是占用的字节空间不同。
注:MySQL语法是不区分大小写的,操作时使用小写更具可读性。
tinyint存储的整数类型,大小为1个字节,所以有符号的tinyint的存储范围是[-128,127]。
测试代码:
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],测试:
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原理都一样,只是占用字节空间不同而已,在开发过程中按所需空间大小选择就行。
bit类型的存储单位是比特位。
测试代码:
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],在范围外无法插入。
需要使用位图表示数据时可以选用该类型。
语法:float[(m, d)] [unsigned] : m指定显示长度,d指定小数位数,占用空间4个字节。 m表示总长度,,d表示小数位数长度,所以剩下整数位数长度不能超过m-d。
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]。
在做如下测试:
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所以能插入
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测试:
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],其它规则都一样。
double,decimal的使用方法都是一样只是精度不同,decimal的精度更高,float的精度大约为7位,decimal的精度:整数位65,小数位30,这和MySQL版本有关。
测试:
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。
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字节,但它依旧是两字符。
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类型。
存储方式
存储空间
性能比较
填充方式
最大长度
使用场景
表示时间的类型有date、datetime、timestamp
注意timestamp类型不需要我们填写,在我们修改表时系统会自动更新。
测试:
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也是一种字符串对象,用于存储预定义的一组值中的零个或多个值。
测试:
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]超出这个范围无法插入。
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。如下:
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的查找,如下:
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函数,如下:
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>