适用读者:设计表时凭感觉选类型的人。
前置:S11-B01。
实测环境:MySQL 8.0.46(s11lab,2026-11-05)。
覆盖旧文:1213(77) 1214(64) 2597(446) 1727(87) 2595(569) 1728(107) 837(83) 2596(398) 856(83) 1726(82) = 1,996 点。
覆盖扩展:E-S11-003(char/varchar)、E-S11-011(字符集与排序规则)。
上一篇:S11-B01 · 一条 SQL 怎么被执行:连接、解析、优化、执行
下一篇:S11-B03 · 查询。
1. 一句话结论
选类型的核心只有三条:占多少空间、能不能精确比较、会不会隐式转换。字符集选错,索引和比较规则都会跟着错。
2. 实测:char(64) 与 varchar(64) 的元数据
create table c1(c char(64), v varchar(64)) engine=InnoDB;
select column_name, character_maximum_length, character_octet_length
from information_schema.columns where table_schema='s11lab' and table_name='c1';
实测输出:
COLUMN_NAME CHARACTER_MAXIMUM_LENGTH CHARACTER_OCTET_LENGTH
c 64 256
v 64 256
256 = 64 × 4 —— utf8mb4 每个字符最多 4 字节。
| 类型 | 存储 | 尾部空格 | 适用 |
|---|---|---|---|
char(n) |
定长(不足补空格) | 取出时去掉尾部空格(MySQL 行为,待核细节随版本) | 长度固定的短串(性别、状态码) |
varchar(n) |
变长(长度前缀 + 内容) | 保留 | 绝大多数字符串 |
判据:"这个字段的长度会变吗?" 会 → varchar;固定且极短 → char。
3. 字符集与比较规则(E-S11-011)
| 项 | 建议 |
|---|---|
| 字符集 | utf8mb4(不是 utf8——后者只支持 3 字节,存不了 emoji) |
| 排序规则 | utf8mb4_0900_ai_ci(MySQL 8 默认,大小写不敏感) |
| 大小写敏感 | 用 _bin 或 _as_cs |
| 连接字符集 | 与表一致,否则隐式转换 |
"为什么 utf8 存不进 emoji" 是高频问题:MySQL 的 utf8 = utf8mb3(最多 3 字节),emoji 需要 4 字节 → 报错或被截断。
字符集不一致的后果:JOIN 时隐式转换导致索引失效(J02 的失效原因之一)。
4. 数据类型速查
| 类别 | 类型 | 注意 |
|---|---|---|
| 整数 | tinyint/smallint/int/bigint |
够用就好;int(11) 的 11 只是显示宽度(8.0 已废弃) |
| 小数 | decimal(p,s) |
金额必须用 decimal(float/double 有精度误差,S09 实测过) |
| 字符串 | char/varchar/text |
text 不能有默认值、不利索引 |
| 时间 | datetime/timestamp |
timestamp 受时区影响、范围小 |
| 布尔 | tinyint(1) |
MySQL 没有真正的 boolean |
| JSON | json |
可索引需生成列 |
"金额用 float"是最经典的错误——0.1+0.2 != 0.3 在浮点下成立(S09-B02 有实测)。
5. 范式与反范式(A-856)
| 范式 | 要求 | 违反的后果 |
|---|---|---|
| 1NF | 字段原子 | 一个字段存多个值 → 无法索引 |
| 2NF | 非主键完全依赖主键 | 部分依赖 → 更新异常 |
| 3NF | 非主键不依赖其他非主键 | 传递依赖 → 冗余不一致 |
反范式化的判据:"这个冗余能显著减少 JOIN 吗?且更新频率低吗?" 都满足 → 可冗余(如把用户昵称冗余到订单表)。
6. 五个误区
| 误区 | 真相 |
|---|---|
"utf8 就是 utf8mb4" |
utf8 只支持 3 字节 |
"varchar(64) 一定占 64 字符空间" |
变长存储 |
"char 更快所以都用 char" |
长度不固定会浪费空间 |
"金额用 double 没问题" |
精度误差 |
| "范式越高越好" | 读多写少场景常需反范式 |
7. 自检
char(64)与varchar(64)在元数据上有什么相同/不同?256这个数字怎么来的?- 为什么
utf8存不了 emoji? - 金额为什么必须用
decimal? - 什么时候该反范式化?
8. 下一篇
S11-B03 · 查询:WHERE/JOIN/GROUP BY/子查询/分页
建表讲完,下一篇讲查询——重点是"书写顺序不等于执行顺序"这一条。
本篇属于系列《S11 · 数据库与 SQL》。旧文来源:A-1213/1214/2597/1727/2595/1728/837/2596/856/1726;扩展点 E-S11-003/011。元数据为 MySQL 8.0.46 实测。