MySQL: MySQL数据类型详解与选择指南
最后更新:2026-08-26
数据类型决定了数据怎么存、占多大空间、能做什么运算——选错类型后患无穷。
本课系统讲解 MySQL 所有数据类型及选择策略。
graph TB
A[MySQL 数据类型] --> B[整数类型]
A --> C[浮点/定点类型]
A --> D[字符串类型]
A --> E[日期时间类型]
A --> F[JSON 类型]
B --> B1[TINYINT]
B --> B2[SMALLINT]
B --> B3[INT]
B --> B4[BIGINT]
C --> C1[FLOAT]
C --> C2[DOUBLE]
C --> C3[DECIMAL]
D --> D1[CHAR]
D --> D2[VARCHAR]
D --> D3[TEXT]
D --> D4[ENUM]
E --> E1[DATE]
E --> E2[DATETIME]
E --> E3[TIMESTAMP]
1. 你将学到
- 整数类型(TINYINT ~ BIGINT)
- 浮点与定点类型(FLOAT/DOUBLE/DECIMAL)
- 字符串类型(CHAR/VARCHAR/TEXT/ENUM)
- 日期时间类型(DATE/DATETIME/TIMESTAMP)
- JSON 类型(MySQL 8.0+)
2. 一个精度问题的真实故事
(1) 痛点:金额算错了
一个电商系统用 FLOAT 存储商品价格:
SQL
-- 演示浮点精度丢失
SELECT 0.1 + 0.2; -- 结果: 0.300000004(近似值,不精确)
-- 存入表后更明显
CREATE TABLE float_demo (price FLOAT);
INSERT INTO float_demo VALUES (199.99);
SELECT price FROM float_demo; -- 结果: 199.99(显示正确但内部存储有微小误差)
SELECT price + 0.01 FROM float_demo; -- 累积误差可能在运算中暴露
1,000 笔交易后,累积的精度误差让财务对账差了几块钱。
(2) DECIMAL 的解法
SQL
-- DECIMAL 精确存储,无误差
CREATE TABLE decimal_demo (price DECIMAL(10,2));
INSERT INTO decimal_demo VALUES (199.99);
SELECT price + 0.01 FROM decimal_demo; -- 结果: 200.00(精确)
结果:price = 200.00,精确无误差。
| 类型 | 精度 | 适用场景 |
|---|---|---|
| FLOAT | 近似(6-7 位) | 科学计算、不要求精确 |
| DOUBLE | 近似(15-16 位) | 科学计算 |
| DECIMAL | 精确 | 金额、财务数据 |
3. 整数类型
| 类型 | 字节 | 有符号范围 | 无符号范围 |
|---|---|---|---|
| TINYINT | 1 | -128 ~ 127 | 0 ~ 255 |
| SMALLINT | 2 | -32,768 ~ 32,767 | 0 ~ 65,535 |
| MEDIUMINT | 3 | -8,388,608 ~ 8,388,607 | 0 ~ 16,777,215 |
| INT | 4 | -2,147,483,648 ~ 2,147,483,647 | 0 ~ 4,294,967,295 |
| BIGINT | 8 | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 | 0 ~ 18,446,744,073,709,551,615 |
▶ 示例:整数类型
SQL
CREATE TABLE user_stats (
id INT PRIMARY KEY AUTO_INCREMENT,
age TINYINT UNSIGNED, -- 年龄 0-255 足够
view_count INT UNSIGNED, -- 浏览次数
total_orders BIGINT UNSIGNED -- 总订单数(可能很大)
);
-- UNSIGNED 表示无符号(只有正数)
INSERT INTO user_stats (age, view_count, total_orders)
VALUES (25, 1000000, 9999999999);
💡 提示: 选择够用的最小类型,节省存储空间。年龄用 TINYINT,用户 ID 用 BIGINT。
4. 浮点与定点类型
| 类型 | 字节 | 精度 | 适用场景 |
|---|---|---|---|
| FLOAT | 4 | 6-7 位 | 科学计算 |
| DOUBLE | 8 | 15-16 位 | 科学计算 |
| DECIMAL(M,D) | 可变 | 精确 | 金额、财务 |
M = 总位数,D = 小数位数。
DECIMAL(10,2)表示最多 10 位整数 + 2 位小数。
▶ 示例:DECIMAL 用法
SQL
CREATE TABLE financials (
id INT PRIMARY KEY,
amount DECIMAL(12,2), -- 金额,最大 9999999999.99
rate DECIMAL(5,4), -- 比率,最大 9.9999
quantity DECIMAL(8,0) -- 整数数量
);
INSERT INTO financials VALUES (1, 1234567.89, 0.0850, 1000);
SELECT * FROM financials;
输出:
TEXT
📖 仅展示
+----+-------------+--------+----------+
| id | amount | rate | quantity |
+----+-------------+--------+----------+
| 1 | 1234567.89 | 0.0850 | 1000 |
+----+-------------+--------+----------+
5. 字符串类型
| 类型 | 最大长度 | 特点 | 适用场景 |
|---|---|---|---|
| CHAR(N) | 255 字节 | 固定长度,速度快 | 手机号、身份证、MD5 |
| VARCHAR(N) | 65535 字节 | 可变长度,节省空间 | 用户名、邮箱、标题 |
| TINYTEXT | 255 字节 | 文本 | 短文本 |
| TEXT | 65535 字节 | 文本 | 文章内容 |
| MEDIUMTEXT | 16M | 文本 | 长文章 |
| LONGTEXT | 4G | 文本 | 超长文本 |
| ENUM | 65535 个值 | 枚举 | 状态、类型 |
| SET | 64 个值 | 集合 | 多选标签 |
(1) CHAR vs VARCHAR
SQL
-- CHAR(10) 固定长度,存储 'abc' 占 10 字节
-- VARCHAR(10) 可变长度,存储 'abc' 占 4 字节(3+1长度前缀)
CREATE TABLE phones (
-- 手机号固定 11 位,用 CHAR 更快
mobile CHAR(11),
-- 用户名长度不定,用 VARCHAR 更省
username VARCHAR(50)
);
▶ 示例:ENUM 枚举类型
SQL
CREATE TABLE orders (
id INT PRIMARY KEY,
status ENUM('pending', 'paid', 'shipped', 'completed', 'cancelled') DEFAULT 'pending'
);
INSERT INTO orders (id, status) VALUES (1, 'paid');
INSERT INTO orders (id) VALUES (2); -- 默认 'pending'
-- ENUM 存储的是数字索引(1,2,3...),不是字符串
SELECT status, status + 0 AS index_num FROM orders;
输出:
TEXT
📖 仅展示
+---------+-----------+
| status | index_num |
+---------+-----------+
| paid | 2 |
| pending | 1 |
+---------+-----------+
6. 日期时间类型
| 类型 | 格式 | 范围 | 字节 |
|---|---|---|---|
| DATE | YYYY-MM-DD | 1000-01-01 ~ 9999-12-31 | 3 |
| TIME | HH:MM:SS | -838:59:59 ~ 838:59:59 | 3 |
| DATETIME | YYYY-MM-DD HH:MM:SS | 1000-01-01 ~ 9999-12-31 | 8 |
| TIMESTAMP | YYYY-MM-DD HH:MM:SS | 1970-01-01 ~ 2038-01-19 | 4 |
| YEAR | YYYY | 1901 ~ 2155 | 1 |
(1) DATETIME vs TIMESTAMP
| 维度 | DATETIME | TIMESTAMP |
|---|---|---|
| 范围 | 1000~9999 年 | 1970~2038 年 |
| 存储 | 8 字节 | 4 字节 |
| 时区 | 不转换 | 自动转 UTC |
| 推荐 | 历史日期 | 记录创建/修改时间 |
▶ 示例:日期时间用法
SQL
CREATE TABLE events (
id INT PRIMARY KEY AUTO_INCREMENT,
event_name VARCHAR(100),
event_date DATE,
start_time TIME,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
INSERT INTO events (event_name, event_date, start_time)
VALUES ('Tech Conference', '2026-09-15', '09:00:00');
SELECT * FROM events;
输出:
TEXT
📖 仅展示
+----+----------------+------------+------------+---------------------+---------------------+
| id | event_name | event_date | start_time | created_at | updated_at |
+----+----------------+------------+------------+---------------------+---------------------+
| 1 | Tech Conference| 2026-09-15 | 09:00:00 | 2026-07-03 10:00:00 | 2026-07-03 10:00:00 |
+----+----------------+------------+------------+---------------------+---------------------+
7. JSON 类型(MySQL 8.0+)
▶ 示例:JSON 操作
SQL
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
attributes JSON
);
INSERT INTO products (name, attributes) VALUES
('iPhone', '{"color": "black", "storage": 128, "tags": ["phone", "apple"]}');
-- 提取 JSON 值
SELECT
name,
attributes->>'$.color' AS color,
attributes->>'$.storage' AS storage
FROM products;
-- JSON 数组查询
SELECT * FROM products WHERE JSON_CONTAINS(attributes->'$.tags', '"apple"');
输出:
TEXT
📖 仅展示
+--------+-------+---------+
| name | color | storage |
+--------+-------+---------+
| iPhone | black | 128 |
+--------+-------+---------+
8. 数据类型选择最佳实践
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 主键 ID | BIGINT AUTO_INCREMENT | 范围大,性能好 |
| 用户名 | VARCHAR(50) | 可变长度,节省空间 |
| 邮箱 | VARCHAR(100) | 可变长度 |
| 手机号 | CHAR(11) | 固定长度,查询快 |
| 密码哈希 | CHAR(60) | bcrypt 固定 60 字符 |
| 金额 | DECIMAL(10,2) | 精确计算 |
| 状态 | ENUM 或 TINYINT | 有限选项 |
| 文章内容 | TEXT | 长文本 |
| 创建时间 | TIMESTAMP | 自动管理,4字节 |
| JSON 数据 | JSON | 支持索引和函数 |
❓ 常见问题
Q VARCHAR(255) 和 VARCHAR(256) 有什么区别?
A VARCHAR 长度 ≤255 用 1 字节存长度,>255 用 2 字节。没有性能本质区别,但 255 是常用分界点。
Q TIMESTAMP 2038 年问题怎么办?
A MySQL 8.0 已经在规划修复。如果担心,用 DATETIME 代替。
Q 一个字段可以存多个值吗?
A 可以用 SET 类型或 JSON 数组,但推荐拆成关联表(更规范)。
Q TEXT 字段可以建索引吗?
A 可以,但只能建前缀索引(
INDEX(col(100)))。如果需要完整索引,考虑用 VARCHAR。📖 小节
- 整数类型按范围选择:TINYINT→SMALLINT→INT→BIGINT
- 金额必须用 DECIMAL,不能用 FLOAT/DOUBLE
- CHAR 固定长度(手机号),VARCHAR 可变长度(用户名)
- ENUM 适合有限选项(状态、类型)
- TIMESTAMP 适合记录时间,DATETIME 适合业务日期
- JSON 类型支持灵活的半结构化数据
📝 作业
-
基础题(难度⭐):为电商系统设计
products表,选择合适的数据类型存储名称、价格、库存、状态、描述。 -
进阶题(难度⭐⭐):创建一个
user_profiles表,包含 JSON 类型的preferences字段,练习 JSON 值的提取和查询。 -
挑战题(难度⭐⭐⭐):对比 DECIMAL(10,2) 和 FLOAT 在存储金额时的精度差异,用实验证明 DECIMAL 的精确性。