MySQL全数据类型从数值、字符串到时间详解(手把手选型避坑)
作者:小叶-duck
前言
在 MySQL 库表设计工作里,字段数据类型的选取,会直接左右数据库的存储空间占用、索引查询效率与数据存储可靠性。合理选定数据类型,可以有效压缩磁盘占用、杜绝数值溢出、规避浮点精度丢失等常见故障;倘若类型选用不当,极易埋下长期隐患,例如使用整型存储超长手机号而造成数据截断。本文会逐一拆解 MySQL 全部常用数据类型,搭配落地测试案例梳理选型思路,带你摆脱随意定义字段的习惯,熟练掌握规范、高效的数据类型使用方法。
一、MySQL 数据类型体系整体梳理与分类介绍
MySQL 的数据类型丰富,按功能可分为五大类,覆盖数值、字符串、日期、特殊类型等场景:
| 分类 | 核心类型 | 适用场景 |
|---|---|---|
| 数值类型 | BIT、TINYINT、INT、BIGINT、FLOAT、DECIMAL | 存储数字(年龄、金额、计数等) |
| 字符串类型 | CHAR、VARCHAR、TEXT、BLOB | 存储文本(姓名、地址、大文本、二进制数据) |
| 日期时间类型 | DATE、DATETIME、TIMESTAMP | 存储时间(生日、创建时间、时间戳) |
| 特殊字符串 | ENUM(枚举)、SET(集合) | 固定选项(性别、爱好、状态等) |
| 二进制类型 | BLOB | 存储图片、文件等二进制数据 |

二、数值类型:依照数值区间与精度要求合理选定类型
数值类型是最常用的类型,核心关注范围和精度,避免数据溢出或精度丢失。
2.1 整数类型:BIT、TINYINT、INT、BIGINT 特性解析
整数类型按占用字节和范围分为 5 类,支持 UNSIGNED(无符号)修饰(默认有符号):
| 类型 | 占用字节 | 有符号范围 | 无符号范围 | 适用场景 |
|---|---|---|---|---|
| BIT(M) | 1-8 | 1-64 位(默认 1 位) | 同有符号 | 存储 0/1(性别)、位掩码 |
| TINYINT | 1 | -128 ~ 127 | 0 ~ 255 | 年龄、状态值(0-255) |
| SMALLINT | 2 | -32768 ~ 32767 | 0 ~ 65535 | 小范围计数(如订单编号) |
| INT | 4 | -2147483648 ~ 2147483647 | 0 ~ 4294967295 | 普通计数(如用户ID) |
| BIGINT | 8 | -9e18 ~ 9e18 | 0 ~ 1.8e19 | 大数值(如手机号、学生ID) |
关键实战要点
谨慎使用 UNSIGNED 无符号修饰,工程中尽量不使用:无符号仅能拓宽正数上限,存在明显缺陷:插入负数会直接触发报错、且与有符号类型计算时容易出现逻辑问题;同时无符号整型溢出无平滑容错,线上故障概率更高。 扩容优先选用更大的基础整型,例如需要 0~300 范围,直接用 INT,而非 TINYINT UNSIGNED,兼容性与稳定性更优。
BIT 类型局限性强,日常业务尽量少用:BIT 用于存放二进制位,查询结果会按照 ASCII 字符展示,可读性极差:BIT (8) 存入 10 会显示换行符、存入 65 会展示字符 A,调试排查十分麻烦。 它仅适合存储少量 0/1 标记、位掩码,不要用来存放常规数字,布尔状态优先选用 TINYINT (1) 替代 BIT。
2.1.1 TINYINT 类型测试
TINYINT 占用 1 字节,有符号范围 - 128~127,无符号范围 0~255:
-- 1. 创建有符号TINYINT表 CREATE TABLE test_tinyint1(age TINYINT); -- 2. 插入合法值 INSERT INTO test_tinyint1 VALUES(127); -- 成功(最大值) INSERT INTO test_tinyint1 VALUES(-128); -- 成功(最小值) -- 3. 插入越界值(报错) INSERT INTO test_tinyint1 VALUES(128); -- 报错:Out of range value for column 'age' at row 1 -- 4. 创建无符号TINYINT表 CREATE TABLE test_tinyint2(age TINYINT UNSIGNED); -- 5. 插入无符号合法值 INSERT INTO test_tinyint2 VALUES(255); -- 成功(无符号最大值) -- 6. 插入负数(无符号越界报错) INSERT INTO test_tinyint2 VALUES(-1); -- 报错:Out of range value for column 'age' at row 1



2.1.2 BIT 类型测试
BIT 存储位数据,默认 1 位(仅支持 0/1),位数 M 需 ≤ 64,对于老版本的 MySQL 查询时会按 ASCII 码显示(易踩坑),而对于较新版本的 MySQL 会显示成16进制,大家可以自己去试试:
-- 1. 创建BIT(1)字段的表 CREATE TABLE test_bit(gender BIT(1)); -- 2. 插入合法值(0/1) INSERT INTO test_bit VALUES(0); -- 成功 INSERT INTO test_bit VALUES(1); -- 成功 -- 3. 插入越界值(报错) INSERT INTO test_bit VALUES(2); -- 报错:Data truncation: Data too long for column 'gender' at row 1 -- 4. 查询BIT字段(关键:直接查询显示ASCII字符,需转成数字) SELECT gender, bin(gender+0) FROM test_bit;
运行结果:
+--------+---------------+ | gender | bin(gender+0) | +--------+---------------+ | | 0 | -- 0的ASCII显示为空白,转数字后是0 | | 1 | -- 1的ASCII显示为,转数字后是1 +--------+---------------+



2.1.3 INT/BIGINT 对比测试
-- 1. INT存储手机号(越界测试) CREATE TABLE test_int(phone INT); INSERT INTO test_int VALUES(13800138000); -- 报错:Out of range value for column 'phone' at row 1(INT最大值2147483647 < 13800138000) -- 2. BIGINT存储手机号(成功) CREATE TABLE test_bigint(phone BIGINT); INSERT INTO test_bigint VALUES(13800138000); -- 成功 SELECT * FROM test_bigint; +-------------+ | phone | +-------------+ | 13800138000 | +-------------+
2.2 小数类型:FLOAT、DOUBLE、DECIMAL 辨析
小数类型用于存储带小数点的数值,核心区别在于精度:
| 类型 | 占用字节 | 精度特性 | 适用场景 |
|---|---|---|---|
| FLOAT | 4 | 单精度,约 7 位有效数字 | 非精确计算(如身高、体重) |
| DOUBLE | 8 | 双精度,约 15 位有效数字 | 较高精度计算(如温度) |
| DECIMAL | 可变 | 高精度(自定义整数 + 小数位) | 精确计算(如金额、税率) |
2.2.1 FLOAT 类型测试
语法:
float[(m, d)] [unsigned] : -- M指定显示长度,d指定小数位数,占用空间4个字节
有符号 float 测试:
mysql> create table tt6(id int, salary float(4,2)); Query OK, 0 rows affected (0.01 sec) mysql> insert into tt6 values(100, -99.99); Query OK, 1 row affected (0.00 sec) mysql> insert into tt6 values(101, -99.991); -- 多的这一点被拿掉了 Query OK, 1 row affected (0.00 sec) mysql> select * from tt6; +------+--------+ | id | salary | +------+--------+ | 100 | -99.99 | | 101 | -99.99 | +------+--------+ 2 rows in set (0.00 sec)


无符号 float 测试:
mysql> create table tt7(id int, salary float(4,2) unsigned); Query OK, 0 rows affected (0.01 sec) mysql> insert into tt7 values(100, -0.1); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> show warnings; +---------+------+-------------------------------------------------+ | Level | Code | Message | +---------+------+-------------------------------------------------+ | Warning | 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)

2.2.2 DECIMAL 类型测试

2.2.3 FLOAT 与 DECIMAL 精度误差对照实验
-- 1. 创建小数测试表 CREATE TABLE test_decimal( salary_float FLOAT(10,8), salary_decimal DECIMAL(10,8) ); -- 2. 插入相同数值 INSERT INTO test_decimal VALUES(23.12345612, 23.12345612); -- 3. 查询结果(精度差异) SELECT * FROM test_decimal;
运行结果:
+---------------+----------------+ | salary_float | salary_decimal | +---------------+----------------+ | 23.12345695 | 23.12345612 | +---------------+----------------+
- 发现 decimal 的精度更准确,因此如果我们希望某个数据表示高精度,选择 decimal。
- 关键结论:FLOAT 是近似存储,存在精度丢失;DECIMAL 是精确存储,适合金额、税率等场景。
- float 表示的精度大约是7位,decimal 整数最大位置 m 为65,支持小数最大位置 d 是30,如果 d 被省略,默认位0,如果 m 被省略,默认是10。建议如果希望小数的精度高,推荐使用decimal。

三、字符串类型:CHAR 与 VARCHAR 场景抉择
字符串类型是最容易用错的类型,核心是区分 CHAR(固定长度)和 VARCHAR(可变长度),以及 TEXT(大文本)的适用场景。
3.1 核心字符串类型对比
| 类型 | 长度限制 | 存储特性 | 适用场景 |
|---|---|---|---|
| CHAR(L) | L ≤ 255(字符) | 固定长度,不足自动补空格 | 长度固定内容(身份证、手机号) |
| VARCHAR(L) | L ≤ 65535(字节) | 可变长度,额外占用1~3字节记录长度 | 长度不固定文本(姓名、地址) |
| TEXT | 最大 65535 字节 | 大文本类型,不支持默认值、普通索引 | 长文本(文章内容、大段备注) |
| BLOB | 最大 65535 字节 | 二进制原始存储 | 图片、文件等二进制数据 |
3.2 CHAR 类型测试
语法:
char(L): -- 固定长度字符串,L是可以存储的长度,单位为字符,最大长度值可以为255
实战案例:
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。
mysql> create table tt10(id int ,name char(256)); ERROR 1074 (42000): Column length too big for column 'name' (max = 255); use BLOB or TEXT instead

3.3 VARCHAR 类型测试
语法:
varchar(L): -- 可变长度字符串,L表示字符长度,最大长度65535个字节
实战案例:
mysql> create table tt10(id int ,name varchar(6)); --表示这里可以存放6个字符 mysql> insert into tt10 values(100, 'hello'); mysql> insert into tt10 values(100, '我爱你,中国'); mysql> select * from tt10; +------+--------------------+ | id | name | +------+--------------------+ | 100 | hello | | 100 | 我爱你,中国 | +------+--------------------+

3.4 CHAR、VARCHAR 存储机制深度实测
-- 1. 创建CHAR/VARCHAR对比表(UTF8编码,1字符=3字节)
CREATE TABLE test_char_varchar(
c_char CHAR(4),
c_varchar VARCHAR(4)
) CHARSET=utf8;
-- 2. 插入不同长度数据
INSERT INTO test_char_varchar VALUES('abcd', 'abcd'); -- 4字符
INSERT INTO test_char_varchar VALUES('A', 'A'); -- 1字符
INSERT INTO test_char_varchar VALUES('中国', '中国'); -- 2字符
-- 3. 查询数据(查看实际存储)
SELECT
c_char, LENGTH(c_char), -- CHAR长度:固定12字节(4×3)
c_varchar, LENGTH(c_varchar) -- VARCHAR长度:实际字符×3 + 1字节
FROM test_char_varchar;
运行结果:
+--------+---------------+------------+------------------+ | c_char | LENGTH(c_char)| c_varchar | LENGTH(c_varchar)| +--------+---------------+------------+------------------+ | abcd | 12 | abcd | 13 | | A | 12 | A | 4 | | 中国 | 6 | 中国 | 7 | +--------+---------------+------------+------------------+
关键结论:
- CHAR (4) 无论存储多少字符,都占用 12 字节(4×3),不足补空格;
- VARCHAR (4) 存储 1 字符占用 4 字节(3+1),2 字符占用 7 字节(6+1),4 字符占用 13 字节(12+1),更节省空间。

如何选择定长或变长字符串?
- 如果数据确定长度都一样,就使用定长(char),比如:身份证,手机号,md5
- 如果数据长度有变化,就使用变长(varchar), 比如:名字,地址,但是你要保证最长的能存的进去。
- 定长的磁盘空间比较浪费,但是效率高。
- 变长的磁盘空间比较节省,但是效率低。
- 定长的意义是,直接开辟好对应的空间
- 变长的意义是,在不超过自定义范围的情况下,用多少,开辟多少。
四、日期时间类型:DATE / DATETIME / TIMESTAMP 选型方案
日期时间类型用于存储时间相关数据,核心是区分三者的存储范围、时区特性和自动更新机制:
| 类型 | 占用字节 | 时间范围 | 时区特性 | 自动更新 | 适用场景 |
|---|---|---|---|---|---|
| DATE | 3 | 1000-01-01~9999-12-31 | 无 | 无 | 生日、日期记录 |
| DATETIME | 8 | 1000-01-01~9999-12-31 | 无 | 无 | 固定时间(如订单创建时间) |
| TIMESTAMP | 4 | 1970-01-01~2038-01-19 | 受时区影响 | 支持自动更新 | 时间戳(如最后修改时间) |
4.1 TIMESTAMP 自动更新特性实操演示
-- 1. 创建日期测试表
CREATE TABLE test_datetime(
t1 DATE,
t2 DATETIME,
t3 TIMESTAMP
);
-- 2. 仅插入DATE/DATETIME,TIMESTAMP自动填充当前时间
INSERT INTO test_datetime(t1,t2) VALUES('1997-07-01','2008-08-08 12:01:01');
-- 3. 查询初始结果
SELECT * FROM test_datetime;
运行结果 1:
+------------+---------------------+---------------------+ | t1 | t2 | t3 | +------------+---------------------+---------------------+ | 1997-07-01 | 2008-08-08 12:01:01 | 2024-05-20 15:30:22 | -- t3自动填充当前时间 +------------+---------------------+---------------------+
-- 4. 更新数据,TIMESTAMP自动刷新 UPDATE test_datetime SET t1='2000-01-01'; -- 5. 查询更新结果 SELECT * FROM test_datetime;
运行结果 2:
+------------+---------------------+---------------------+ | t1 | t2 | t3 | +------------+---------------------+---------------------+ | 2000-01-01 | 2008-08-08 12:01:01 | 2024-05-20 15:35:48 | -- t3自动更新为修改时间 +------------+---------------------+---------------------+

五、 特殊字符串类型:ENUM 单选、SET 多选场景落地
ENUM(枚举)和 SET(集合)适用于固定选项的场景,避免手动校验输入合法性。

5.1 ENUM 枚举单选类型用法实测
-- 1. 创建枚举表
CREATE TABLE test_enum(
username VARCHAR(30),
gender ENUM('男','女')
);
-- 2. 插入合法值
INSERT INTO test_enum VALUES('雷锋','男'); -- 成功
INSERT INTO test_enum VALUES('小红',2); -- 成功(2对应第二个选项'女')
-- 3. 插入非法值(报错)
INSERT INTO test_enum VALUES('Tom','未知');
-- 报错:Data truncated for column 'gender' at row 1
-- 4. 查询枚举值
SELECT username, gender, gender+0 FROM test_enum; -- gender+0查看枚举对应的数字
运行结果:
+----------+--------+-----------+ | username | gender | gender+0 | +----------+--------+-----------+ | 雷锋 | 男 | 1 | | 小红 | 女 | 2 | +----------+--------+-----------+


5.2 SET 集合多选类型用法实测
-- 1. 创建集合表
CREATE TABLE test_set(
username VARCHAR(30),
hobby SET('登山','游泳','篮球','武术')
);
-- 2. 插入多选值
INSERT INTO test_set VALUES('雷锋','登山,武术'); -- 成功
INSERT INTO test_set VALUES('小红','游泳'); -- 成功(单选)
-- 3. 插入非法值(自动忽略)
INSERT INTO test_set VALUES('Tom','登山,游戏'); -- '游戏'不是选项,仅插入'登山'查询指定爱好的语句:
mysql> select * from votes where hobby='登山'; +----------+-----------+ | username | hobby | +----------+-----------+ | Tom | 登山 | +----------+-----------+
问题:不能查询出所有,爱好为登山的人。
集合查询使用 find_ in_ set 函数:
find_in_set(sub, str_list):如果 sub 在 str_list 中,则返回下标;如果不在,返回0; str_list 用逗号分隔的字符串。
函数使用案例:
mysql> select find_in_set('a', 'a,b,c');
+---------------------------+
| find_in_set('a', 'a,b,c') |
+---------------------------+
| 1 |
+---------------------------+
mysql> select find_in_set('d', 'a,b,c');
+---------------------------+
| find_in_set('d', 'a,b,c') |
+---------------------------+
| 0 |
+---------------------------+查询所有包含但不仅限于爱好登山的人:
-- 4. 查询包含某选项的记录(find_in_set函数)
SELECT * FROM test_set WHERE FIND_IN_SET('登山', hobby);运行结果:
+----------+-----------+ | username | hobby | +----------+-----------+ | 雷锋 | 登山,武术 | | Tom | 登山 | +----------+-----------+





六、数据类型选型经验汇总与常见踩坑规避
- 按 “最小必要” 原则选型:如年龄用 TINYINT(0-255 足够),不用 INT;金额用 DECIMAL(10,2),不用 FLOAT。
- 避免用字符串存储数字:如手机号用 BIGINT 或 CHAR(11),不用 VARCHAR(11)(CHAR查询更快);身份证用 CHAR(18)(固定长度),不用 VARCHAR。
- BIT 类型查询需转数字:直接查询 BIT 字段显示 ASCII 字符,需用 gender+0 或 hex(gender) 转为数字。
- 慎用 TEXT/BLOB:大文本类型会降低查询性能,若能拆分字段(如将文章摘要单独存储为VARCHAR),尽量避免直接用T EXT。
- 日期类型优先选 DATETIME:除非明确需要时区转换或自动更新,否则 DATETIME 的兼容性和范围更优,避免 TIMESTAMP 的 2038 限制。
总结: MySQL 数据类型的选择核心是 “匹配场景 + 平衡性能与空间”
- BIT/TINYINT 有严格范围限制,插入越界值会直接报错,查询 BIT 需转数字;
- FLOAT 存在精度丢失,金额等精确场景必须用 DECIMAL;
- CHAR 适合固定长度字符串(如手机号),VARCHAR 适合可变长度字符串(如姓名);
- TIMESTAMP 支持自动更新,但有 2038 年限制,普通场景优先用 DATETIME;
- ENUM/SET 简化固定选项存储,SET 查询需用 find_in_set 函数。
结束语
本篇系统梳理了 MySQL 常用数据类型的分类、底层存储特性与实战测试,从整数、浮点高精度数值,到定长 / 变长字符串、日期时间、枚举集合等类型逐一拆解,对比了不同类型的取值范围、空间开销、精度缺陷与使用边界。
日常建库建表时,很多性能隐患、数据错乱、存储空间冗余问题,根源往往都是数据类型随意选取:一味用大类型造成空间浪费、选用浮点类型存储金额引发精度丢失、滥用 TIMESTAMP 遭遇 2038 时限陷阱、VARCHAR 长度乱定义浪费索引效率。
MySQL 数据类型是数据库优化的基石,熟练掌握各类类型的优劣与适用场景,后续索引设计、SQL 调优、分库分表等进阶优化才能落地生根。后续我们可以结合索引、事务等内容,继续深挖 MySQL 底层原理,不断夯实数据库工程实践能力。
到此这篇关于MySQL全数据类型从数值、字符串到时间的文章就介绍到这了,更多相关MySQL数值、字符串到时间详解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
