字段设计必须在建表前确定,类型错、约束漏、默认值乱会导致90%的脏数据和慢查询;应选用最小够用类型,避免全用VARCHAR(255),否则引发索引失效、范围查询失败、排序错乱等问题。

字段设计不是“先写完再改”,而是建表前必须定死的事——类型错、约束漏、默认值乱,后面90%的脏数据和慢查询都源于此。
用最小够用的数据类型,别全上 VARCHAR(255)
新手最常犯的错误是把所有字段都设成 VARCHAR(255),连年龄、状态、性别都字符串存。这直接导致:索引失效(字符串索引比整数慢且占空间大)、无法做范围查询(WHERE age > 18 在字符串字段上走不了索引)、排序错乱('100'
-
age用TINYINT UNSIGNED(0–255),比INT节省 3 字节,且业务上不可能超 200 岁 -
gender用ENUM('男','女')或TINYINT(0/1),禁止填入 '未知' 'other' 等非法值 -
score别用FLOAT,改用DECIMAL(5,2)(如 99.50),避免精度丢失 -
phone固定 11 位手机号,用CHAR(11)比VARCHAR更快(无长度头开销),且能天然校验长度
非空约束 NOT NULL 是默认选项,不是可选动作
MySQL 中 NULL 不是“空值”,而是“未知值”——它不参与任何比较(WHERE status = NULL 永远不成立),会破坏索引选择性(B+树中 NULL 不进索引叶节点),还会让 COUNT(*) 和 COUNT(col) 行为不一致。
- 除极少数明确允许缺失的字段(如
emergency_contact),其余字段一律加NOT NULL - 配合
DEFAULT使用:is_active TINYINT NOT NULL DEFAULT 1,而不是留空靠应用层补 - 主键、外键、唯一索引列,MySQL 强制要求
NOT NULL,但别依赖这个隐式规则,显式写出更清晰
AUTO_INCREMENT 主键必须配 INT 或 BIGINT,别用字符串
用 VARCHAR 当主键(比如 UUID 字符串)看似灵活,实际代价极高:索引体积暴增(36 字符 vs 4/8 字节)、插入时 B+ 树频繁分裂、JOIN 性能下降明显。除非业务强依赖分布式 ID 且已做好分库分表,否则别碰。
- 单机或中小规模系统,
id BIGINT PRIMARY KEY AUTO_INCREMENT是最稳选择 -
AUTO_INCREMENT必须搭配PRIMARY KEY,且字段类型只能是整型(TINYINT到BIGINT) - 不要手动
INSERT INTO t(id) VALUES (100)插入自增字段值,除非你清楚auto_increment_offset和auto_increment_increment的影响 - 如果真要用 UUID,至少用
BINARY(16)存UNHEX(REPLACE(uuid(), '-', '')),而不是原生字符串
DEFAULT 值要写明,但别滥用 CURRENT_TIMESTAMP 多次
DEFAULT CURRENT_TIMESTAMP 很方便,但 MySQL 8.0 之前只允许一个 TIMESTAMP 字段用它;8.0+ 虽支持多个,但 ON UPDATE CURRENT_TIMESTAMP 只能绑定一个字段——若两个时间字段都设了,第二个会静默失效。
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMPupdated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP- 别给
created_at加ON UPDATE,否则插入后第一次更新就会被覆盖 - 字符串字段慎用
DEFAULT '',它和NULL语义不同,且可能掩盖业务逻辑缺陷(比如邮箱为空字符串而非未提供)
真正难的不是记住语法,而是每次建字段前问一句:这个值业务上是否可能为空?是否需要参与计算或排序?最大长度是否真会到 255?——答案决定类型、约束、默认值,一步错,步步错。


















