MySQL 数据类型的选择直接影响 “存储效率”“查询性能” 和 “数据准确性”—— 用int存手机号会导致截断,用float存金额会出现精度丢失,用char存变长字符串会浪费空间。本文将系统梳理 MySQL 的核心数据类型(数值、字符串、日期、枚举等),结合实战案例解析每种类型的适用场景、边界限制和选型建议,帮你精准选择数据类型,写出更健壮的 SQL。
一、数据类型分类:先搞懂整体框架
MySQL 数据类型按功能可分为 5 大类,不同类别适配不同业务场景,先建立整体认知:
| 分类 | 包含类型 | 核心用途 |
|---|---|---|
| 数值类型 | BIT、TINYINT、SMALLINT、INT、BIGINT、FLOAT、DOUBLE、DECIMAL | 存储整数、小数(如 ID、年龄、金额) |
| 字符串类型 | CHAR、VARCHAR、BLOB、TEXT | 存储文本(如用户名、地址、大文本) |
| 日期时间类型 | DATE、DATETIME、TIMESTAMP | 存储日期、时间(如生日、创建时间) |
| 枚举类型 | ENUM | 单选场景(如性别、订单状态) |
| 集合类型 | SET | 多选场景(如爱好、标签) |
二、数值类型:选对长度,避免越界与浪费
数值类型是最常用的类型,核心需关注 “是否带符号”“占用字节” 和 “范围”,避免因长度不足导致数据截断,或长度过大浪费空间。
2.1 整数类型:按需选择,拒绝 “一刀切”
整数类型按 “占用字节” 从小到大分为 TINYINT、SMALLINT、INT、BIGINT,默认带符号(支持负数),可通过UNSIGNED指定无符号(仅正数)。
整数类型对比表
| 类型 | 占用字节 | 带符号范围 | 无符号范围 | 适用场景 |
|---|---|---|---|---|
| TINYINT | 1 | -128 ~ 127 | 0 ~ 255 | 状态值(如 0 = 禁用、1 = 启用)、年龄 |
| SMALLINT | 2 | -32768 ~ 32767 | 0 ~ 65535 | 小范围整数(如班级人数、商品库存) |
| INT | 4 | -2147483648 ~ 2147483647 | 0 ~ 4294967295 | 常规 ID(用户 ID、订单 ID)、数量 |
| BIGINT | 8 | -9e18 ~ 9e18 | 0 ~ 1.8e19 | 超大整数(如雪花 ID、时间戳毫秒数) |
实战要点
- 避免越界:插入值超过类型范围会报错,例如
TINYINT插入 128 会提示 “Out of range”; - 慎用 UNSIGNED:若数据确实无负数(如年龄),可加
UNSIGNED扩大上限(如TINYINT UNSIGNED最大 255),但建议优先用更大类型(如用INT存年龄,避免后续业务扩展时重构); - 案例:存储用户年龄(最大 150),用
TINYINT UNSIGNED足够;存储订单 ID(可能超 20 亿),必须用BIGINT。
2.2 位类型(BIT):节省空间的 “开关” 类型
BIT(M)用于存储位数据,M指定位数(1~64,默认 1),适合存储 “开关” 类数据(如 0/1 表示禁用 / 启用)。
实战案例
-- 创建BIT(1)字段,存储性别(0=女,1=男)
CREATE TABLE user_gender (
id INT,
gender BIT(1)
);
-- 插入合法数据
INSERT INTO user_gender VALUES (1, 0), (2, 1);
-- 插入越界数据(2超出BIT(1)范围),报错
INSERT INTO user_gender VALUES (3, 2); -- Error 1406: Data too long
注意事项
显示问题:BIT字段查询时按 ASCII 码显示(如BIT(8)插入 65 会显示 'A'),需用HEX()函数查看原始值;
适用场景:仅用于存储 0/1 或少量位组合(如权限掩码),不适合存储常规整数。
2.3 小数类型:精度优先选 DECIMAL,性能优先选 FLOAT
小数类型分为FLOAT(单精度)、DOUBLE(双精度)、DECIMAL(定点数),核心差异在 “精度”——FLOAT/DOUBLE有精度损失,DECIMAL是精确存储。
小数类型对比表
| 类型 | 占用字节 | 精度 | 适用场景 |
|---|---|---|---|
| FLOAT | 4 | 约 7 位有效数字 | 非精确小数(如温度、体重) |
| DOUBLE | 8 | 约 15~17 位有效数字 | 较高精度需求(如科学计算) |
| DECIMAL | 可变 | 精确到指定小数位(M,D) | 高精度场景(如金额、汇率) |
实战案例:精度差异演示
-- 创建表对比FLOAT和DECIMAL
CREATE TABLE price (
id INT,
float_price FLOAT(10,8), -- 单精度,10位总长,8位小数
decimal_price DECIMAL(10,8) -- 定点数,10位总长,8位小数
);
-- 插入相同数值
INSERT INTO price VALUES (1, 23.12345612, 23.12345612);
-- 查询结果:FLOAT有精度损失,DECIMAL精确
SELECT * FROM price;
-- 输出:id=1, float_price=23.12345695, decimal_price=23.12345612
选型建议
金额、汇率等精确场景:必须用DECIMAL(M,D),如DECIMAL(10,2)存储金额(支持 - 99999999.99 ~ 99999999.99);
非精确场景:用FLOAT/DOUBLE(性能更高,占用空间更小),如存储商品评分、温度。
三、字符串类型:CHAR 与 VARCHAR 的抉择
字符串类型中,CHAR(固定长度)和VARCHAR(可变长度)是日常开发的重点,选错会导致空间浪费或性能损耗。
3.1 CHAR:固定长度,效率优先
CHAR(L)存储固定长度字符串,L为字符数(最大 255),无论实际存储内容长度,均占用L×字符字节数空间(如 UTF8 中 1 个字符占 3 字节)。
特点
优点:存储 / 查询效率高(无需计算长度),适合存储长度固定的数据;
缺点:空间浪费(如CHAR(10)存 “abc” 仍占用 30 字节);
适用场景:手机号(11 位)、身份证号(18 位)、MD5 值(32 位)等长度固定的数据。
3.2 VARCHAR:可变长度,空间优先
VARCHAR(L)存储可变长度字符串,L为最大字符数(受编码影响:UTF8 最大 21844,GBK 最大 32766),实际占用 “数据长度 + 1~3 字节长度标识”(存储数据长度)。
特点
优点:节省空间(如VARCHAR(10)存 “abc” 仅占用 3×3 + 1 = 10 字节);
缺点:存储 / 查询需额外计算长度,效率略低于CHAR;
适用场景:用户名(2~20 位)、地址(不定长)、商品描述等长度可变的数据。
3.3 CHAR 与 VARCHAR 对比实战
以 UTF8 编码(1 字符 = 3 字节)为例,存储不同长度的字符串:
| 存储内容 | CHAR (4) 占用字节 | VARCHAR (4) 占用字节 | 结论 |
|---|---|---|---|
| "abcd" | 4×3 = 12 | 4×3 + 1 = 13 | CHAR 更省空间 |
| "a" | 4×3 = 12 | 1×3 + 1 = 4 | VARCHAR 更省空间 |
| "中国" | 4×3 = 12 | 2×3 + 1 = 7 | VARCHAR 更省空间 |
选型口诀
长度固定 → 用CHAR(如手机号、MD5);
长度可变 → 用VARCHAR(如用户名、地址);
超 255 字符 → 用TEXT(如文章内容)。
3.4 大字符串类型(BLOB/TEXT)
当字符串长度超VARCHAR上限(如文章、日志),需用BLOB(二进制)或TEXT(文本):
TEXT:存储大文本,不支持全文索引和默认值,分为TINYTEXT(255 字符)、TEXT(65535 字符)、LONGTEXT(4GB);
BLOB:存储二进制数据(如图片、文件),不推荐直接存储文件,建议存文件路径(用VARCHAR),文件放服务器或对象存储(如 OSS)。
四、日期时间类型:选对类型,避免时区与精度坑
日期时间类型用于存储时间相关数据,核心有DATE(日期)、DATETIME(日期 + 时间)、TIMESTAMP(时间戳),需关注 “范围”“时区” 和 “自动更新” 特性。
4.1 日期时间类型对比表
| 类型 | 格式 | 占用字节 | 范围 | 核心特性 |
|---|---|---|---|---|
| DATE | 'YYYY-MM-DD' | 3 | 1000-01-01 ~ 9999-12-31 | 仅存日期(如生日) |
| DATETIME | 'YYYY-MM-DD HH:MM:SS' | 8 | 1000-01-01 ~ 9999-12-31 | 存日期 + 时间,不受时区影响 |
| TIMESTAMP | 'YYYY-MM-DD HH:MM:SS' | 4 | 1970-01-01 ~ 2038-01-19 | 存时间戳,受时区影响,可自动更新 |
4.2 实战案例:自动更新时间戳
TIMESTAMP支持 “插入 / 更新时自动填充当前时间”,适合存储 “创建时间”“更新时间”:
CREATE TABLE user (
id INT,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 插入时自动填当前时间
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -- 更新时自动更新
);
-- 插入数据:无需指定create_time和update_time
INSERT INTO user (id) VALUES (1);
-- 查询结果:create_time和update_time均为当前时间
SELECT * FROM user;
-- 更新数据:update_time自动更新为当前时间
UPDATE user SET id=2 WHERE id=1;
SELECT * FROM user; -- update_time已更新
4.3 选型建议
仅需日期:用DATE(如用户生日);
需日期 + 时间,无时区需求:用DATETIME(如订单支付时间);
需自动更新时间:用TIMESTAMP(如创建 / 更新时间);
注意 2038 问题:TIMESTAMP最大到 2038 年,需存储更晚时间用DATETIME。
五、枚举与集合类型:简化单选与多选场景
当数据值是固定选项(如性别、爱好),用ENUM(单选)或SET(多选)可简化业务逻辑,避免脏数据。
5.1 ENUM:单选枚举类型
ENUM('选项1','选项2',...)存储单选值,实际存储为数字(1、2...),最多支持 65535 个选项。
实战案例:存储性别
CREATE TABLE user_info (
id INT,
gender ENUM('男','女','未知') -- 单选,仅允许这三个值
);
-- 插入合法数据
INSERT INTO user_info VALUES (1, '男'), (2, '女');
-- 插入非法数据:报错
INSERT INTO user_info VALUES (3, '其他'); -- Error 1265: Data truncated
-- 也可通过数字插入(不推荐,可读性差)
INSERT INTO user_info VALUES (4, 3); -- 对应'未知'
5.2 SET:多选集合类型
SET('选项1','选项2',...)存储多选值,选项用逗号分隔,实际存储为位图(1、2、4、8...),最多支持 64 个选项。
实战案例:存储爱好
CREATE TABLE user_hobby (
id INT,
hobby SET('登山','游泳','篮球','武术') -- 多选,可选多个值
);
-- 插入多选数据(用逗号分隔)
INSERT INTO user_hobby VALUES (1, '登山,武术'), (2, '游泳');
-- 查询:查找喜欢登山的用户(需用FIND_IN_SET函数)
SELECT * FROM user_hobby WHERE FIND_IN_SET('登山', hobby);
-- 输出:id=1(爱好为登山,武术)
注意事项
SET查询需用FIND_IN_SET(sub, str)函数(如FIND_IN_SET('登山', hobby)),直接用hobby='登山'只能查到仅选 “登山” 的记录;
选项值不能包含逗号(会与分隔符冲突)。
六、数据类型选型总原则
- 最小够用原则:用最小的类型满足业务需求(如用
TINYINT存状态,不用INT); - 精度优先原则:金额、汇率等精确数据用
DECIMAL,不用FLOAT/DOUBLE; - 可读性原则:枚举用字符串值(如
'男'),不用数字(如 1),便于维护; - 未来扩展原则:预留一定长度(如用
VARCHAR(60)存用户名,避免后续不够用); - 避免过度设计:不用
BIT存常规整数,不用SET存动态标签(用关联表更灵活)。
七、总结
MySQL 数据类型的选择没有 “最优解”,只有 “最适合的解”—— 理解每种类型的边界、精度和适用场景,才能避免存储浪费、数据丢失等问题。本文梳理的核心类型对比表和选型建议,可作为日常开发的 “字典”,遇到不确定的场景时快速查阅,确保数据存储的高效与准确。
1311

被折叠的 条评论
为什么被折叠?



