文章目录
-
- [1. 数据类型分类](#1. 数据类型分类)
- [2. 数值类型](#2. 数值类型)
-
- [2.1 tinyint 类型](#2.1 tinyint 类型)
- [2.2 bit 类型](#2.2 bit 类型)
- [2.3 小数类型](#2.3 小数类型)
-
- [2.3.1 float](#2.3.1 float)
- [2.3.2 decimal](#2.3.2 decimal)
- [3. 字符串类型](#3. 字符串类型)
-
- [3.1 char](#3.1 char)
- [3.2 varchar](#3.2 varchar)
- [3.3 char和varchar比较](#3.3 char和varchar比较)
- [4. 日期和时间类型](#4. 日期和时间类型)
- [5. enum和set](#5. enum和set)
-
- [5.1 集合查询使用 find_ in_ set 函数:](#5.1 集合查询使用 find_ in_ set 函数:)
1. 数据类型分类
下图展示的是数据库中常见的数据类型分类及说明:

2. 数值类型
下面是关于数据库中整数类型(TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT)的字节数、最小值和最大值(带符号 / 无符号)的表格:

2.1 tinyint 类型
在测试之前,我们还是先建一个库:
sql
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| RECOVER_YOUR_DATA |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> create database test_db;
Query OK, 1 row affected (0.00 sec)
mysql> use test_db;
Database changed
然后再建一张表:
sql
mysql> create table if not exists t1(
-> num tinyint
-> );
Query OK, 0 rows affected (0.02 sec)
接着,我们有三种方式可以查看表结构:

现在进行数值越界测试,先插入范围内的值:
sql
mysql> insert into t1 values (-128);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t1 values (127);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1 values (0);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t1 values (-1);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t1 values (1);
Query OK, 1 row affected (0.00 sec)
mysql> select * from t1;
+------+
| num |
+------+
| -128 |
| 127 |
| 0 |
| -1 |
| 1 |
+------+
5 rows in set (0.01 sec)
结果如下:

再插入范围之外的值:
sql
mysql> insert into t1 values (-129);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> insert into t1 values (128);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> insert into t1 values (130);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
结果如下:

说明:
- 在 MySQL 中,整型可以指定是有符号的和无符号的,默认是有符号的。
- 可以通过 UNSIGNED 来说明某个字段是无符号的。
无符号案例如下(先建一张无符号的表):
sql
mysql> create table if not exists t2(
-> num tinyint unsigned
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> desc t2;
+-------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------------------+------+-----+---------+-------+
| num | tinyint(3) unsigned | YES | | NULL | |
+-------+---------------------+------+-----+---------+-------+
1 row in set (0.01 sec)
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(100);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t2 values(-1);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
结果如下:

说明:
- 如果我们向 MySQL 特定的类型中插入不合法的数据,那么 MySQL 一般都是直接拦截我们的,不让我们做对应的操作。
- 相反,如果我们已经有数据被成功插入到 MySQL 中了,那么一定插入的时候是合法的。
- 所以,在 MySQL 中,一般而言,数据类型本身也是一种 "约束"。这种 "约束" 是让 "使用者" 尽可能的进行正确的插入。
注意:
- 尽量不使用 unsigned,对于 int 类型可能存放不下的数据,int unsigned 同样可能存放不下,与其如此,还不如设计时,将 int 类型提升为 bigint 类型。
2.2 bit 类型
基本语法:
sql
bit[(M)] : 位字段类型。M表示每个值的位数,范围从1到64。如果M被忽略,默认为1。
我们还是先建表
sql
mysql> create table if not exists t3(
-> id int,
-> online bit(1)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> desc t3;
+--------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| online | bit(1) | YES | | NULL | |
+--------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)
然后再插入数据:
sql
mysql> insert into t3 (id, online) values (123, 0);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t3 (id, online) values (124, 1);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t3 (id, online) values (124, 3);
ERROR 1406 (22001): Data too long for column 'online' at row 1
mysql> insert into t3 (id, online) values (124, 5);
ERROR 1406 (22001): Data too long for column 'online' at row 1
mysql> insert into t3 (id, online) values (124, 2);
ERROR 1406 (22001): Data too long for column 'online' at row 1
因为我们的 online 设置的是 1 个 bit 位,所以只能是 0 / 1,其余数字都不行。
另外,我们查表可以看到 id 能正常显示,online 却无法显示出来:

因为 online 的类型是比特位嘛,它在这里显示时,其实是通常会给我们按照 ASCII 码值的形式显示的。只不过当前 online 这里对应的 ASCII 码值是不可显示的。
我们可以用十进制的方式来显示 bit 位:

我们知道,bit 字段在显示时,是按照 ASCII 码对应的值显示的。但是如何验证呢?
我们先更改 t3 表中 online 的类型:
sql
mysql> alter table t3 modify online bit(10);
Query OK, 2 rows affected (0.04 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> desc t3;
+--------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| online | bit(10) | YES | | NULL | |
+--------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)
然后再插入数据:
sql
mysql> insert into t3 (id, online) values (125, 6);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t3 (id, online) values (127, 5);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t3 (id, online) values (128, 'a');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t3 (id, online) values (129, 97);
Query OK, 1 row affected (0.00 sec)
结果如下,可以看到,我们插入 'a' 和 97 的效果是一样的:

另外,如果不指定 bit 的范围,那么默认就是 bit(1):

如果我们有这样的值,只存放 0 或 1,这时可以定义 bit(1),这样可以节省空间。
2.3 小数类型
2.3.1 float
语法:
sql
float[(m, d)] [unsigned] : M指定显示长度,d指定小数位数,占用空间4个字节
还是先建立一张表:
sql
mysql> create table if not exists t5 (
-> id int,
-> salary float(4,2)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> desc t5;
+--------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| salary | float(4,2) | YES | | NULL | |
+--------+------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
案例一:float(4,2)
表示的范围是 -99.99 ~ 99.99
,MySQL 在保存值时会进行 四舍五入。
sql
mysql> insert into t5 (id, salary) values (1, 99.99);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t5 (id, salary) values (1, -99.99);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t5 (id, salary) values (2, -99.991);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t5 (id, salary) values (2, -99.996);
ERROR 1264 (22003): Out of range value for column 'salary' at row 1
mysql> insert into t5 (id, salary) values (2, -99.995);
ERROR 1264 (22003): Out of range value for column 'salary' at row 1
mysql> insert into t5 (id, salary) values (3, -99.994);
Query OK, 1 row affected (0.00 sec)
结果如下:

那么思考一下: 当我们的 float(4,2)
如果是一个有符号的,则表示范围是 -99.99 ~ 99.99
,如果 float(6,3)
,请问范围是多少?
答案是:
-999.999 ~ 999.999
案例二:如果定义的是 float(4,2) unsigned
这时,因为把它指定为无符号的数,范围是 0 ~ 99.99
。
还是先建表:
sql
mysql> create table if not exists t6 (
-> id bigint,
-> salary float(4,2) unsigned
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> desc t6;
+--------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------------------+------+-----+---------+-------+
| id | bigint(20) | YES | | NULL | |
| salary | float(4,2) unsigned | YES | | NULL | |
+--------+---------------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
然后再插入数据
sql
mysql> insert into t6 (id, salary) values (1,0);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t6 (id, salary) values (2,99.99);
Query OK, 1 row affected (0.01 sec)
mysql> insert into t6 (id, salary) values (3,99.994);
Query OK, 1 row affected (0.00 sec)
mysql> insert into t6 (id, salary) values (4,99.995);
ERROR 1264 (22003): Out of range value for column 'salary' at row 1
mysql> insert into t6 (id, salary) values (4,-99.99);
ERROR 1264 (22003): Out of range value for column 'salary' at row 1
mysql> insert into t6 (id, salary) values (4,34.567);
Query OK, 1 row affected (0.01 sec)
mysql> select * from t6;
+------+--------+
| id | salary |
+------+--------+
| 1 | 0.00 |
| 2 | 99.99 |
| 3 | 99.99 |
| 4 | 34.57 |
+------+--------+
4 rows in set (0.00 sec)
结果如下:

2.3.2 decimal
语法:
sql
decimal(m, d) [unsigned] : 定点数m指定长度,d表示小数点的位数
说明:
decimal(5,2)
表示的范围是-999.99 ~ 999.99
decimal(5,2) unsigned
表示的范围0 ~ 999.99
- decimal 和 float 很像,但是有区别,它们表示的精度不一样
还是先建立一张表:
sql
mysql> create table if not exists t7 (
-> id int,
-> f1 float(10, 8),
-> f2 decimal(10, 8)
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> desc t7;
+-------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| f1 | float(10,8) | YES | | NULL | |
| f2 | decimal(10,8) | YES | | NULL | |
+-------+---------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
案例如下:
sql
mysql> insert into t7 (id, f1, f2) values (111, 23.12345612, 23.12345612);
Query OK, 1 row affected (0.00 sec)
mysql> select * from t7;
+------+-------------+-------------+
| id | f1 | f2 |
+------+-------------+-------------+
| 111 | 23.12345695 | 23.12345612 |
+------+-------------+-------------+
1 row in set (0.00 sec)
可以发现 decimal 的精度更准确,因此如果我们希望某个数据表示高精度,选择 decimal。

说明:
- float 表示的精度大约是 7 位。
- decimal 整数最大位数 m 为 65。支持小数最大位数 d 是 30。如果 d 被省略,默认为 0,如果 m 被省略,默认是 10。
3. 字符串类型
3.1 char
语法:
sql
char(L): 固定长度字符串,L是可以存储的长度,单位为字符,最大长度值可以为255
先建表:
sql
mysql> create table if not exists t8 (
-> id int,
-> name char(2)
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> desc t8;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | char(2) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)
案例:
sql
mysql> insert into t8 (id, name) values (100, 'a');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t8 (id, name) values (101, 'ab');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t8 (id, name) values (102, 'abc');
ERROR 1406 (22001): Data too long for column 'name' at row 1
mysql> insert into t8 (id, name) values (103, '你');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t8 (id, name) values (104, '你好');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t8 (id, name) values (105, '你好吗');
ERROR 1406 (22001): Data too long for column 'name' at row 1
mysql>
mysql> select * from t8;
+------+--------+
| id | name |
+------+--------+
| 100 | a |
| 101 | ab |
| 103 | 你 |
| 104 | 你好 |
+------+--------+
4 rows in set (0.00 sec)
结果如下:

说明:char(2)
表示可以存放两个字符,可以是字母或汉字,但是不能超过 2 个, 最多只能是 255。

3.2 varchar
语法:
sql
varchar(L): 可变长度字符串,L表示字符长度,最大长度65535个字节
案例:
sql
mysql> create table if not exists t9 (
-> id int,
-> name varchar(6)
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> insert into t9 (id, name) values (111, 'hello');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t9 (id, name) values (222, '我爱你,世界');
Query OK, 1 row affected (0.00 sec)
mysql> insert into t9 (id, name) values (333, '我爱你,大帅哥');
ERROR 1406 (22001): Data too long for column 'name' at row 1
mysql> select * from t9;
+------+--------------------+
| id | name |
+------+--------------------+
| 111 | hello |
| 222 | 我爱你,世界 |
+------+--------------------+
2 rows in set (0.00 sec)
结果如下:

关于 varchar(len)
中的 len
到底是多大,这个 len 值,和表的编码密切相关:
- varchar 的字节长度可以指定为 0 到 65535 之间的值,但是有 1 - 3 个字节用于记录数据大小,所以说有效字节数是65532。
- 当我们的表的编码是 utf8 时,
varchar(n)
的参数 n 最大值是65532/3=21844
(因为 utf 中,一个字符占用 3 个字节),如果编码是 gbk,varchar(n)
的参数 n 最大是65532/2=32766
(因为 gbk 中,一个字符占用 2 字节)。
我们现在可以验证一下 utf8 确实是不能超过 21844:

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 年开始的yyyy-mm-dd HH:ii:ss
格式和datetime
完全一致,占用 4 字节。
先创建表:
sql
mysql> create table if not exists t11 (
-> t1 date,
-> t2 datetime,
-> t3 timestamp
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> desc t11;
+-------+-----------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------+------+-----+-------------------+-----------------------------+
| t1 | date | YES | | NULL | |
| t2 | datetime | YES | | NULL | |
| t3 | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------+-----------+------+-----+-------------------+-----------------------------+
3 rows in set (0.00 sec)
再插入数据:
sql
mysql> insert into t11 (t1, t2) values ('2008-10-01', '2010-10-01 8:30:05');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t11;
+------------+---------------------+---------------------+
| t1 | t2 | t3 |
+------------+---------------------+---------------------+
| 2008-10-01 | 2010-10-01 08:30:05 | 2025-10-16 12:47:53 |
+------------+---------------------+---------------------+
1 row in set (0.00 sec)
添加数据时,时间戳自动补上当前时间,结果如下:

那么我们现在可以更新 t1 的数据,可以看到更新数据,时间戳会更新成当前时间:

5. enum和set
enum:枚举,"单选" 类型;
sql
enum('选项1','选项2','选项3',...);
说明:
- 该设定只是提供了若干个选项的值,最终一个单元格中,实际只存储了其中一个值;
- 而且出于效率考虑,这些值实际存储的是 "数字",因为这些选项的每个选项值依次对应如下数字:
1,2,3,....
,最多65535个; - 当我们添加枚举值时,也可以添加对应的数字编号。
set:集合,"多选"类型;
sql
set('选项值1','选项值2','选项值3', ...);
说明:
- 该设定只是提供了若干个选项的值,最终一个单元格中,设计可存储了其中任意多个值;
- 而且出于效率考虑,这些值实际存储的是 "数字",因为这些选项的每个选项值依次对应如下数字:
1,2,4,8,16,32,....
,最多64个。
另外需要注意:不建议在添加枚举值,集合值的时候采用数字的方式,因为不利于阅读。
案例:有一个调查表 votes,需要调查人的喜好, 比如(登山,游泳,篮球,武术)中去选择(可以多选),其中性别 男 / 女 是单选。
sql
mysql> create table if not exists votes (
-> username varchar(30),
-> gender enum('男', '女'),
-> hobby set('登山', '游泳', '篮球', '唱歌', '写代码')
-> );
Query OK, 0 rows affected (0.01 sec)
mysql> desc votes;
+----------+------------------------------------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------------------------------------------------+------+-----+---------+-------+
| username | varchar(30) | YES | | NULL | |
| gender | enum('男','女') | YES | | NULL | |
| hobby | set('登山','游泳','篮球','唱歌','写代码') | YES | | NULL | |
+----------+------------------------------------------------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
插入数据:
sql
mysql> insert into votes values ('张三', '男', '写代码');
Query OK, 1 row affected (0.00 sec)
mysql> insert into votes values ('李四', '女', '唱歌,篮球');
Query OK, 1 row affected (0.00 sec)
mysql> select * from votes;
+----------+--------+---------------+
| username | gender | hobby |
+----------+--------+---------------+
| 张三 | 男 | 写代码 |
| 李四 | 女 | 篮球,唱歌 |
+----------+--------+---------------+
2 rows in set (0.00 sec)
另外对于 enum 枚举类型,我们可以使用下标的方式进行插入(下标从 1 开始)
sql
mysql> insert into votes values ('王五', 1, '登山,游泳');
Query OK, 1 row affected (0.00 sec)
mysql> insert into votes values ('赵六', 2, '登山,游泳');
Query OK, 1 row affected (0.00 sec)
mysql> select * from votes;
+----------+--------+---------------+
| username | gender | hobby |
+----------+--------+---------------+
| 张三 | 男 | 写代码 |
| 李四 | 女 | 篮球,唱歌 |
| 王五 | 男 | 登山,游泳 |
| 赵六 | 女 | 登山,游泳 |
+----------+--------+---------------+
4 rows in set (0.00 sec)
结果如下:

同样,我们使用数字标识每个 爱好 的时候,能不能用这种方法呢?

其实这里就和 enum 不一样了,那么想想 Linux 权限,采用比特位的位置来和 set 中的 爱好 对应起来,也就是比特位的位置就代表 爱好:

那么同时插入 5 个爱好,其实就是 2 5 − 1 2^5-1 25−1,也就是 31:

使用如下查询语句,但是该语句不能查询出所有,爱好为登山的人。
sql
mysql> select * from votes where hobby='登山';
+----------+--------+--------+
| username | gender | hobby |
+----------+--------+--------+
| 胡九 | 男 | 登山 |
+----------+--------+--------+
1 row in set (0.00 sec)
结果如下:

5.1 集合查询使用 find_ in_ set 函数:
find_in_set(sub,str_list)
:如果 sub 在 str_list
中,则返回下标;如果不在,返回 0;
其中 str_list
用逗号分隔的字符串。
可以看到字符 a
在字符串 a, b, c
中,故返回结果为 1;同理,如果不在,返回结果为 0。

那如果我要查询爱好为 登山 的所有人呢?
sql
mysql> select * from votes where find_in_set('登山', hobby);
+----------+--------+---------------------------------------+
| username | gender | hobby |
+----------+--------+---------------------------------------+
| 王五 | 男 | 登山,游泳 |
| 赵六 | 女 | 登山,游泳 |
| 田七 | 男 | 登山,游泳,写代码 |
| 胡九 | 男 | 登山 |
| tom | 男 | 登山 |
| andy | 男 | 登山,游泳 |
| Rolly | 男 | 登山,游泳,篮球,唱歌,写代码 |
+----------+--------+---------------------------------------+
7 rows in set (0.00 sec)
结果如下:

还可以查询爱好中既有登山,又有游泳的用户:
