根本原因是字段定义为not null且无default值,insert时又未显式赋值,严格模式在“值填充阶段”校验失败即抛错er_no_default_for_field,而非语法或权限问题。

MySQL 开启严格模式后 INSERT 空值报错,根本原因不是“不允许空”,而是「字段定义为 NOT NULL 且未提供值,又没有合法默认值可填充」——此时 MySQL 拒绝隐式补 null/0/'',直接中断写入。
STRICT_TRANS_TABLES 怎么拦截 INSERT
它在 INSERT 执行的「值填充阶段」介入:解析完语句、映射列之后,逐个检查缺失字段。若某列满足 NOT NULL + 无 DEFAULT + INSERT 语句未显式赋值,就抛出 ER_NO_DEFAULT_FOR_FIELD(错误码 1364 或 1048),例如 Field 'id' doesn't have a default value。
这不是语法错误,SQL 能正常解析;也不是权限问题,是数据完整性校验主动拒绝。
- 仅影响缺失值的列,其他列正常写入
- 对已定义
DEFAULT的列,仍会按默认值填充(但需确保该默认值本身合法,比如DATE DEFAULT '0000-00-00'在NO_ZERO_DATE下也会失败) -
STRICT_TRANS_TABLES只作用于事务型引擎(如 InnoDB),MyISAM 行为可能不同
常见触发场景和对应字段类型
报错不只出现在 INT 主键上,任何 NOT NULL 且无默认值的字段都可能中招:
-
INT类型写'':报Incorrect integer value: '' for column 'id' -
VARCHAR类型写NULL(而字段不允许 NULL):报Field 'name' doesn't have a default value -
DATETIME字段依赖隐式零日期填充(如DEFAULT '0000-00-00'),在NO_ZERO_DATE下会提前在建表或 DML 阶段报错Invalid default value - ORM 自动生成 INSERT(如 Laravel 的
create([])、Django 的Model.objects.create())若没传全字段,极易暴露该问题
为什么不能简单 SET SESSION sql_mode = ''
临时清空 sql_mode 虽能绕过报错,但会同时关闭所有严格校验:
-
INSERT INTO t(col) VALUES ('abcde')往VARCHAR(3)插 5 个字符,不再报错,而是静默截断 → 数据被悄悄破坏 -
INSERT INTO t(num) VALUES ('xyz')往INT插字符串,不再报错,而是转成 0 → 业务逻辑错乱 - 会话级设置对连接池、多线程应用不可控,容易漏设或误设
- 掩盖真实数据结构缺陷,后续迁移、分库、审计时问题集中爆发
真正该改什么:字段定义优先于 SQL 模式
修复方向必须落在表结构或应用层,而非降级数据库安全水位:
- 主键
id应加AUTO_INCREMENT:用ALTER TABLE t MODIFY id INT AUTO_INCREMENT PRIMARY KEY;,之后INSERT INTO t(name) VALUES ('a')就能自增生成 - 非主键非空字段(如
status)应补默认值:ALTER TABLE t ALTER COLUMN status SET DEFAULT 'active';或MODIFY status TINYINT DEFAULT 1; - 确认允许为空的字段,直接改结构:
ALTER TABLE t MODIFY col VARCHAR(20) NULL; - 应用层 INSERT 必须显式列出所有
NOT NULL字段并赋值,避免依赖隐式行为
严格模式本意是暴露设计漏洞。一旦发现 Field xxx doesn't have a default value,第一反应不该是关模式,而是查 SHOW CREATE TABLE —— 那里藏着真正要动的字段定义。











