必须启用strict_trans_tables或strict_all_tables才能阻止dml非法数据静默截断等行为;only_full_group_by和no_zero_date仅分别约束select分组和零日期,对not null插null、varchar超长截断、tinyint溢出等dml问题完全无效。

必须启用 STRICT_TRANS_TABLES 或 STRICT_ALL_TABLES,否则任何其他 sql_mode 配置都拦不住 INSERT / UPDATE 中的非法数据静默截断、溢出修正或 NULL 转默认值。
为什么只加 ONLY_FULL_GROUP_BY 或 NO_ZERO_DATE 没用
这些模式各管一摊,完全不干预 DML 写入行为:
-
ONLY_FULL_GROUP_BY只在SELECT+GROUP BY时校验字段来源,对插入毫无影响 -
NO_ZERO_DATE仅拦截'0000-00-00'这种字面零日期,但不会阻止向TINYINT插入300(仍会转成127或警告) - 向
NOT NULL字段插NULL、向VARCHAR(5)插'hello world'、向DATE插'2026-00-15'—— 全部绕过,除非开了严格模式
STRICT_TRANS_TABLES 和 STRICT_ALL_TABLES 怎么选
优先用 STRICT_TRANS_TABLES,它只对事务表(如 InnoDB)生效,对 MyISAM 表保持原宽松行为:
- 若业务混合使用 InnoDB 和 MyISAM,
STRICT_TRANS_TABLES更可控;STRICT_ALL_TABLES会让 MyISAM 的超长字符串也报错,可能触发旧脚本批量失败 - 两者都不影响
SELECT,只作用于INSERT、UPDATE、REPLACE - 如果全库用 InnoDB,二者效果一致;若有遗留 MyISAM 表且无法改造,
STRICT_TRANS_TABLES是更稳妥的选择
永久配置必须改 my.cnf 并重启,临时设置无效
会话级 SET SESSION sql_mode = '...' 或全局级 SET GLOBAL sql_mode = '...' 都不能用于生产防护:
- 会话级设置只对当前连接有效,应用重连后就失效
- 全局级设置不生效于已存在的连接,且 MySQL 重启后丢失
- 必须编辑
/etc/my.cnf(Linux)或my.ini(Windows)的[mysqld]段,写成:sql_mode = "STRICT_TRANS_TABLES,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO" - 注意:等号右侧必须用英文双引号包裹,漏引号会导致 MySQL 启动失败
启用后 ORM 和旧脚本容易突然报错
严格模式不是“开关一开就万事大吉”,它会暴露长期被掩盖的问题:
- Django/SQLAlchemy 自动生成的
INSERT ... VALUES (DEFAULT, ),在NOT NULL字段无默认值时直接失败 - PHP
mysqli批量插入含空字符串到INT字段(如' '或''),触发类型转换错误而非静默转0 - MySQL 5.7 默认已含
STRICT_TRANS_TABLES,但很多环境手动清空过sql_mode,回退到宽松模式,这种“降级”配置要重点排查
真正起效的严格模式,从来不是加几个参数就完事;它要求你同步检查所有写入路径——尤其是那些没显式指定字段、依赖隐式填充的 INSERT 语句。











