绝大多数字段应设为 NOT NULL 并配合适当 DEFAULT 值,而非 DEFAULT NULL;NOT NULL 强制数据完整性、提升索引效率、避免运算断裂,DEFAULT 则简化插入操作,二者互补而非互斥。

DEFAULT NULL 并不比 NOT NULL 更好——相反,绝大多数字段应该设为 NOT NULL,并搭配合适的 DEFAULT 值(如 ''、0、'1970-01-01')。把字段设成 DEFAULT NULL 是默认行为,但不是推荐实践。
NOT NULL 字段在 INSERT 时会拒绝空值
NOT NULL 的核心作用是强制数据完整性:
- 插入时若该列没给值,且没设
DEFAULT,MySQL 会直接报错(严格模式下); - 即使给了
NULL字面量(如INSERT INTO t (x) VALUES (NULL)),也会被拦截; - 这能避免“本该有值却意外为空”的脏数据进入数据库。
常见错误现象:
- 应用层漏传字段,后端没校验,数据库默默存进
NULL,后续查询或聚合出错(比如SUM(age)忽略所有NULL行,结果偏低); -
WHERE status = 'active'查不到status为NULL的行,但业务上可能期望它们被归为“未初始化”而非“不存在”。
所以,NOT NULL 不是限制灵活性,而是把“空值语义是否合理”这个决策提前到建表阶段。
DEFAULT NULL 实际上是隐式陷阱
MySQL 默认所有字段都是 DEFAULT NULL,除非你显式覆盖。但这带来几个实际问题:
- 索引效率下降:B+ 树索引不存储
NULL值,导致WHERE col IS NULL或ORDER BY col无法高效利用索引; - 统计信息失真:优化器对含大量
NULL的列做基数估算不准,可能选错执行计划; - 运算逻辑断裂:
100 + NULL→NULL,COALESCE(NULL, 'N/A')要处处补; - 应用层负担加重:每个 SQL 都得考虑
IS NULL分支,ORM 映射也容易出空指针。
你不需要“允许 NULL 来保留未知状态”——业务上真正需要表达“未知”的场景极少;更多时候,''(字符串)、0(数值)、'1970-01-01'(时间)已经足够表意,且参与运算安全、索引友好、查询直白。
NOT NULL DEFAULT 组合才是生产环境标配
NOT NULL 和 DEFAULT 不冲突,而是互补:
-
NOT NULL拦住非法空写入; -
DEFAULT在 INSERT 语句省略该列时自动填充,降低应用侧拼 SQL 复杂度。
无序列表说明典型配置:
- 字符串字段:用
NOT NULL DEFAULT '',避免NULL和''混存带来的双重判断; - 数值字段:用
NOT NULL DEFAULT 0,确保加减乘除不出NULL; - 时间字段:用
NOT NULL DEFAULT '1970-01-01 00:00:00'或当前时间函数(如CURRENT_TIMESTAMP),避免IS NULL查询; - 软删除字段:用
is_deleted TINYINT NOT NULL DEFAULT 0,比NULL更明确(0=未删,1=已删); - 主键和外键列:必须
NOT NULL,这是约束本质决定的,DEFAULT可选但通常由自增或关联逻辑保证。
注意:DEFAULT NULL 和 NOT NULL 不能共存(MySQL 会报错),所以“设成 DEFAULT NULL”本身就排除了 NOT NULL。
NOT NULL 不是教条,但它是多数字段最安全的起点。真正需要 NULL 的地方很少——比如“用户可选填的紧急联系人电话”,这时才考虑 phone VARCHAR(20) DEFAULT NULL。但即便如此,也要同步评估:这个字段会不会被索引?会不会参与 JOIN 或聚合?如果答案是“会”,那仍建议用 '' + 应用层语义区分,而不是依赖 NULL。


















