首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >技术干货-MYSQL数据类型详解

技术干货-MYSQL数据类型详解

作者头像
禁默
发布2025-12-24 15:33:24
发布2025-12-24 15:33:24
1410
举报

前言

在 MySQL 数据库设计中,数据类型的选择直接影响数据规范性、存储效率和查询性能。除了 int、varchar、datetime 等基础类型,ENUM(枚举)和 SET(集合)是两种容易被忽略但极具实用价值的特殊字符串类型 —— 它们通过预定义选项限制输入值,既能保证数据一致性,又能节省存储空间。 本文将从数据类型分类切入,先铺垫数值、字符串、日期等基础类型的核心逻辑,再聚焦 ENUM 和 SET 类型的语法定义、使用场景、实战案例,最后通过核心区别对比和注意事项总结,帮你彻底搞懂这两种类型的适用场景,避免在实际开发中踩坑。

数据类型分类

下面是 MySQL 主要数据类型的分类表格,按常用场景分为数值型字符串型日期时间型特殊类型四大类,包含常用类型、说明及适用场景:

类型大类

细分类型

说明(长度 / 范围)

适用场景示例

数值型

TINYINT

1 字节,范围:-128~127(有符号);0~255(无符号,UNSIGNED)

性别(0/1)、状态标识(有限选项)

SMALLINT

2 字节,范围:-32768~32767(有符号);0~65535(无符号)

小范围数量(如班级人数)

MEDIUMINT

3 字节,范围:-8388608~8388607(有符号);0~16777215(无符号)

中等范围数量(如商品库存)

INT/INTEGER

4 字节,范围:-2147483648~2147483647(有符号);0~4294967295(无符号)

整数 ID(用户 ID、订单号)

BIGINT

8 字节,范围:-9e18~9e18(有符号);0~1.8e19(无符号)

大整数(如雪花 ID、海量数据计数)

FLOAT(M,D)

4 字节单精度浮点,M总位数,D小数位(精度有限,可能有误差)

非精确小数(如温度、重量)

DOUBLE(M,D)

8 字节双精度浮点,精度高于FLOAT(仍可能有误差)

科学计算、高精度非精确小数

DECIMAL(M,D)

高精度定点数,M总位数(最大 65),D小数位(最大 30),无精度损失

金额、汇率(需精确计算的数值)

字符串型

CHAR(N)

固定长度字符串,N为长度(1~255),不足补空格,查询时自动截断

短固定长度字符串(如手机号、邮编)

VARCHAR(N)

可变长度字符串,N为最大长度(1~65535,受行总长度限制),仅占实际长度 + 1/2 字节

长度不固定的文本(如姓名、地址)

TEXT

长文本,最大 65535 字节(约 64KB)

短文、描述信息

MEDIUMTEXT

中等长文本,最大 16777215 字节(约 16MB)

文章内容、日志

LONGTEXT

超长文本,最大 4294967295 字节(约 4GB)

大型文档、HTML 内容

ENUM('值1','值2',...)

枚举类型,只能从指定值中选一个(最多 65535 个选项)

固定选项(如性别:男 / 女 / 未知)

SET('值1','值2',...)

集合类型,可多选指定值(最多 64 个选项)

多选项(如兴趣:足球,篮球,阅读)

日期时间型

DATE

日期,格式YYYY-MM-DD,范围 1000-01-01~9999-12-31

生日、订单日期

TIME

时间,格式HH:MM:SS,范围 - 838:59:59~838:59:59

时长、打卡时间点

DATETIME

日期 + 时间,格式YYYY-MM-DD HH:MM:SS,范围 1000-01-01 00:00:00~9999-12-31 23:59:59

事件发生时间(如注册时间)

TIMESTAMP

时间戳,格式同DATETIME,范围 1970-01-01 00:00:01~2038-01-19 03:14:07,受时区影响

记录更新时间(自动更新)

YEAR

年份,格式YYYY,范围 1901~2155 或 0000

出生年份、产品发布年份

特殊类型

BIT(N)

位字段,N为位数(1~64),存储二进制数据

权限标识(每一位代表一个权限)

BLOB

二进制大对象,存储图片、文件等二进制数据(TINYBLOB/BLOB/MEDIUMBLOB/LONGBLOB)

小型图片、二进制文件

JSON

存储 JSON 格式数据,支持索引和 JSON 函数操作

灵活结构数据(如配置、动态属性)

说明:

  1. 数值型的 UNSIGNED 修饰符可取消负数范围,扩大正数上限(如 TINYINT UNSIGNED 范围 0~255)。
  2. 字符串型中,CHAR 适合长度固定的场景(查询效率略高),VARCHAR 适合长度可变的场景(节省空间)。
  3. 日期时间型中,DATETIME 不受时区影响,TIMESTAMP 会随数据库时区自动转换,需根据业务选择。
  4. 实际使用时需根据数据长度、精度要求和业务场景选择最合适的类型,避免过度占用空间或精度不足。

数值类型

tinyint类型

数值越界测试:

代码语言:javascript
复制
mysql> create table tt1;
ERROR 4028 (HY000): A table must have at least one visible column.
mysql> create table tt1(num tinyint);
Query OK, 0 rows affected (0.04 sec)

mysql> insert into ttl values(1);
ERROR 1146 (42S02): Table 'test1.ttl' doesn't exist
mysql> insert into tt1 values(1);
Query OK, 1 row affected (0.01 sec)

mysql> insert into tt1 values(128);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> insert into tt1 values(-128);
Query OK, 1 row affected (0.01 sec)

mysql> insert into tt1 values(-129);
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> insert into tt1 values(127);
Query OK, 1 row affected (0.00 sec)

mysql> select *from tt1;
+------+
| num  |
+------+
|    1 |
| -128 |
|  127 |
+------+
3 rows in set (0.00 sec)

说明: 在MySQL中,整型可以指定是有符号的和无符号的,默认是有符号的。 可以通过UNSIGNED来说明某个字段是无符号的

无符号案例

代码语言:javascript
复制
mysql> create table tt2(num tinyint unsigned);
mysql> insert into tt2 values(-1); -- 无符号,范围是: 0 - 255
ERROR 1264 (22003): Out of range value for column 'num' at row 1
mysql> insert into tt2 values(255);
Query OK, 1 row affected (0.02 sec)
mysql> select * from tt2;
+------+
| num |
+------+
| 255 |
+------+
1 row in set (0.00 sec)

bit类型

代码语言:javascript
复制
bit[(M)] : 位字段类型。M表示每个值的位数,范围从1到64。如果M被忽略,默认为1。

BIT 类型是 MySQL 中高效存储二进制位数据的类型,其显示形式(十六进制)和与 ASCII 码的关联是二进制特性的体现。实际使用中,需根据场景选择:存储二进制标识用 BIT,存储普通数字用 TINYINT/INT,存储字符用 CHAR,避免因显示形式导致误解。

代码语言:javascript
复制
mysql> insert into ttl value(1,1);
ERROR 1146 (42S02): Table 'test.ttl' doesn't exist
mysql> insert into tt1 value(1,1);
Query OK, 1 row affected (0.00 sec)

mysql> select *from tt1;
+------+------------+
| id   | a          |
+------+------------+
|    1 | 0x01       |
+------+------------+
1 row in set (0.00 sec)

mysql> insert into tt1 value(10,10);
Query OK, 1 row affected (0.00 sec)

mysql> select *from tt1;
+------+------------+
| id   | a          |
+------+------------+
|    1 | 0x01       |
|   10 | 0x0A       |
+------+------------+
2 rows in set (0.00 sec)

mysql> insert into tt1 value(65,65);
Query OK, 1 row affected (0.00 sec)

mysql> select *from tt1;
+------+------------+
| id   | a          |
+------+------------+
|    1 | 0x01       |
|   10 | 0x0A       |
|   65 | 0x41       |
+------+------------+
3 rows in set (0.00 sec)

mysql> SELECT id, a, ASCII(a) AS ascii_code FROM tt1;
+------+------------+------------+
| id   | a          | ascii_code |
+------+------------+------------+
|    1 | 0x01       |          1 |
|   10 | 0x0A       |         10 |
|   65 | 0x41       |         65 |
+------+------------+------------+
3 rows in set (0.00 sec)

小数类型

代码语言:javascript
复制
float[(m, d)] [unsigned] : M指定显示长度,d指定小数位数,占用空间4个字节

小数:float(4,2)表示的范围是-99.99 ~ 99.99,MySQL在保存值时会进行四舍五入。

当我们的 float(4,2) 如果是一个有符号的,则表示范围是 -99.99 ~ 99.99 ,如果 float(6,3) ,显而易见是-999.999到999.999

如果定义的是float(4,2) unsigned 这时,因为把它指定为无符号的数,范围是 0 ~ 99.99

代码语言:javascript
复制
mysql> create table tt7(id int, salary float(4,2) unsigned);
Query OK, 0 rows affected, 2 warnings (0.02 sec)

mysql> insert into tt7 values(100, -0.1);
ERROR 1264 (22003): Out of range value for column 'salary' at row 1
mysql> show warnings;
+-------+------+-------------------------------------------------+
| Level | Code | Message                                         |
+-------+------+-------------------------------------------------+
| Error | 1264 | Out of range value for column 'salary' at row 1 |
+-------+------+-------------------------------------------------+
1 row in set (0.00 sec)

mysql> insert into tt7 values(100, -0);
Query OK, 1 row affected (0.00 sec)

mysql> insert into tt7 values(100, 99.99);
Query OK, 1 row affected (0.00 sec)

decimal

代码语言:javascript
复制
decimal(m, d) [unsigned] : 定点数m指定长度,d表示小数点的位数

decimal(5,2) 表示的范围是 -999.99 ~ 999.99

decimal(5,2) unsigned 表示的范围 0 ~ 999.99

decimal和float很像,但是有区别: float和decimal表示的精度不一样

代码语言:javascript
复制
mysql> create table tt8 ( id int, salary float(10,8), salary2
decimal(10,8));
mysql> insert into tt8 values(100,23.12345612, 23.12345612);
Query OK, 1 row affected (0.00 sec)
mysql> select * from tt8;
+------+-------------+-------------+
| id | salary | salary2 |
+------+-------------+-------------+
| 100 | 23.12345695 | 23.12345612 | # 发现decimal的精度更准确,因此如果我们希望某
个数据表示高精度,选择decimal
+------+-------------+-------------+

说明:float表示的精度大约是7位。

decimal整数最大位数m为65。支持小数最大位数d是30。如果d被省略,默认为0.如果m被省略,

默认是10。

建议:如果希望小数的精度高,推荐使用 decimal 。

字符串类型

char

代码语言:javascript
复制
char(L): 固定长度字符串,L是可以存储的长度,单位为字符,最大长度值可以为255
代码语言:javascript
复制
mysql> create table tt9(id int, name char(2));
Query OK, 0 rows affected (0.00 sec)
mysql> insert into tt9 values(100, 'ab');
Query OK, 1 row affected (0.00 sec)
mysql> insert into tt9 values(101, '中国');
Query OK, 1 row affected (0.00 sec)
mysql> select * from tt9;
+------+--------+
| id | name |
+------+--------+
| 100 | ab |
| 101 | 中国 |
+------+--------+

说明:

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

varchar

代码语言:javascript
复制
varchar(L): 可变长度字符串,L表示字符长度,最大长度65535个字节

说明 关于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字节)。

代码语言:javascript
复制
mysql> create table tt11(name varchar(21845))charset=utf8;
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 tt11(name varchar(21844)) charset=utf8;
Query OK, 0 rows affected, 1 warning (0.02 sec)

mysql>

char varchar 比较

如何选择定长或变长字符串?

如果数据确定长度都一样,就使用定长(char),比如:身份证,手机号,md5 如果数据长度有变化,就使用变长(varchar), 比如:名字,地址,但是你要保证最长的能存的进去。 定长的磁盘空间比较浪费,但是效率高。 变长的磁盘空间比较节省,但是效率低。 定长的意义是,直接开辟好对应的空间 变长的意义是,在不超过自定义范围的情况下,用多少,开辟多少。

日期和时间类型

常用的日期有如下三个:

date :日期 'yyyy-mm-dd' ,占用三字节

datetime 时间日期格式 'yyyy-mm-dd HH:ii:ss' 表示范围从 1000 到 9999 ,占用八字节

timestamp :时间戳,从1970年开始的 yyyy-mm-dd HH:ii:ss 格式和 datetime 完全一致,占用

四字节

代码语言:javascript
复制
mysql> create table bir(
    -> t1 date,
    -> t2 datetime,
    -> t3 timestamp
    -> );
Query OK, 0 rows affected (0.02 sec)
代码语言:javascript
复制
mysql> insert into bir (t1,t2)values('2025-10-27','2025-10-27 12:1:1');
Query OK, 1 row affected (0.01 sec)

mysql> select *from bir;
+------------+---------------------+------+
| t1         | t2                  | t3   |
+------------+---------------------+------+
| 2025-10-27 | 2025-10-27 12:01:01 | NULL |
+------------+---------------------+------+
1 row in set (0.00 sec)

mysql> -- 修改表结构,重新定义t3
mysql> ALTER TABLE bir
    -> MODIFY COLUMN t3 timestamp
    -> DEFAULT CURRENT_TIMESTAMP  -- 插入时自动填当前时间
    -> ON UPDATE CURRENT_TIMESTAMP;  -- 更新时自动更新为当前时间
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> select *from bir;
+------------+---------------------+------+
| t1         | t2                  | t3   |
+------------+---------------------+------+
| 2025-10-27 | 2025-10-27 12:01:01 | NULL |
+------------+---------------------+------+
1 row in set (0.00 sec)

mysql> -- 插入新记录(不指定t3)
mysql> INSERT INTO bir (t1, t2) VALUES ('2025-10-28', '2025-10-28 13:00:00');
Query OK, 1 row affected (0.00 sec)

mysql>
mysql> -- 更新已有记录
mysql> UPDATE bir SET t1 = '2025-10-29' WHERE t1 = '2025-10-27';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select *from bir;
+------------+---------------------+---------------------+
| t1         | t2                  | t3                  |
+------------+---------------------+---------------------+
| 2025-10-29 | 2025-10-27 12:01:01 | 2025-10-27 23:27:41 |
| 2025-10-28 | 2025-10-28 13:00:00 | 2025-10-27 23:27:40 |
+------------+---------------------+---------------------+
2 rows in set (0.00 sec)

enum和set

ENUMSET 是 MySQL 中两种特殊的字符串类型,用于存储预定义的离散值,主要区别在于 “单选” 和 “多选”,以及存储方式和使用场景的不同。

ENUM(枚举类型):单选场景

ENUM 用于存储只能从预定义选项中选择一个值的字段(类似 “单选题”)。

代码语言:javascript
复制
CREATE TABLE 表名 (
  字段名 ENUM('选项1', '选项2', '选项3', ...) [约束]
);
  • 选项列表最多支持 65535 个值(实际使用中通常远少于此)。
  • 每个选项对应一个隐藏的数字编号(从 1 开始递增),存储时实际保存的是编号(而非字符串),节省空间。

特点

  • 单选性:插入或更新时,只能选择一个预定义的选项,不能多选。
  • 默认值:若未指定 NOT NULL,默认值为 NULL;若指定 DEFAULT,可设置一个默认选项。
  • 存储优化:以数字编号存储(1 字节),比直接存字符串更高效。
代码语言:javascript
复制
-- 创建包含ENUM字段的表(性别:男/女/保密)
CREATE TABLE user (
  id INT PRIMARY KEY,
  gender ENUM('男', '女', '保密') NOT NULL DEFAULT '保密'
);

-- 插入数据(只能选一个选项)
INSERT INTO user (id, gender) VALUES
(1, '男'),    -- 合法
(2, '女'),    -- 合法
(3, '保密');  -- 合法(使用默认值也可)

-- 错误:插入未定义的选项
INSERT INTO user (id, gender) VALUES (4, '其他');  -- 报错(值不在ENUM列表中)

SET(集合类型):多选场景

SET 用于存储可以从预定义选项中选择多个值的字段(类似 “多选题”),多个值之间用逗号分隔。

代码语言:javascript
复制
CREATE TABLE 表名 (
  字段名 SET('选项1', '选项2', '选项3', ...) [约束]
);
  • 选项列表最多支持 64 个值(受存储方式限制)。
  • 存储时以二进制位的形式保存(每个选项对应一个位),多个选项的组合通过位运算实现,同样节省空间。

特点

  • 多选性:插入或更新时,可以选择多个预定义选项(用逗号分隔)。
  • 默认值:若未指定 NOT NULL,默认值为 NULL;若指定 DEFAULT,可设置一个或多个默认选项(用逗号分隔)。
  • 查询灵活性:可通过 FIND_IN_SET() 函数查询包含特定选项的记录。

综合案例测试

代码语言:javascript
复制
mysql> create table use_hbby(
    -> name varchar(20),
    -> hobby set('登山','游泳','篮球','武术'),
    -> gender enum('男','女') not null);
Query OK, 0 rows affected (0.03 sec)

插入部分数据

代码语言:javascript
复制
mysql> insert into use_hbby values('雷锋', '登山,武术', '男');

mysql> insert into use_hbby values('Juse','登山,武术',2);

mysql> insert into use_hbby values('胡志', '武术', '男');

mysql> insert into use_hbby values('程坤', '游泳', '男');

mysql> insert into use_hbby values('小红','登山','女');

查询数据

代码语言:javascript
复制
mysql> select * from use_hbby where gender=2;
+------+-----------+--------+
| name | hobby     | gender |
+------+-----------+--------+
| Juse | 登山,武术 | 女     |
| 小红 | 登山      | 女     |
+------+-----------+--------+

想查找所有喜欢登山的人

select * from votes where hobby= ' 登山 ' ;不能查询出所有,爱好为登山的人。

集合查询使用 find_ in_ set 函数:

find_in_set(sub,str_list) :如果 sub 在 str_list 中,则返回下标;如果不在,返回 0 ;

str_list 用逗号分隔的字符串。

select * from votes where find_in_set( ' 登山 ' , hobby);

ENUMSET 的核心区别

特性

ENUM

SET

选择方式

单选(只能选一个选项)

多选(可选择多个选项)

选项数量限制

最多 65535 个

最多 64 个

存储方式

数字编号(1 字节)

二进制位(1-8 字节)

典型场景

性别、学历、状态等

兴趣爱好、标签、权限等

插入格式

单个值(如 '男')

多个值用逗号分隔(如 '读书,音乐')

使用注意事项

  1. 选项值的引号:定义时选项必须用单引号或双引号包裹(如 ENUM('男','女'))。
  2. 大小写敏感性:默认不区分大小写(如 '男''男' 视为同一值),但存储时按定义的大小写保存。
  3. 避免滥用:若选项可能频繁变动,不建议使用 ENUMSET(修改选项需 alter 表结构),可考虑用关联表替代。
  4. SET 的查询效率:虽然 FIND_IN_SET() 可以查询,但效率低于普通索引查询,大量数据场景需谨慎。

总结

  • ENUM 适合 “单选” 场景,节省空间且使用简单;
  • SET 适合 “多选” 场景,支持灵活的组合选择;
  • 两者均通过预定义选项限制输入,保证数据规范性,但需根据实际需求选择使用。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 数据类型分类
  • 数值类型
    • tinyint类型
    • bit类型
    • 小数类型
    • decimal
  • 字符串类型
    • char
    • varchar
  • 日期和时间类型
  • enum和set
    • ENUM(枚举类型):单选场景
    • SET(集合类型):多选场景
    • 综合案例测试
    • ENUM 与 SET 的核心区别
    • 使用注意事项
    • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档