MySQL 8.0 默认启用 STRICT_TRANS_TABLES 和 ONLY_FULL_GROUP_BY 是官方主动收紧行为,非配置错误;其新增 NO_ZERO_DATE、NO_ZERO_IN_DATE 等子项,并强制 GROUP BY 符合 SQL 标准,禁用会引发数据截断、零日期隐患及查询结果不可复现等问题。

MySQL 8.0 默认启用 STRICT_TRANS_TABLES 和 ONLY_FULL_GROUP_BY
这不是配置“出错了”,而是官方主动收紧的默认行为。5.7 虽已启用 STRICT_TRANS_TABLES,但 8.0 在此基础上新增了 NO_ZERO_DATE、NO_ZERO_IN_DATE、ERROR_FOR_DIVISION_BY_ZERO 等子项,并将 ONLY_FULL_GROUP_BY 明确纳入默认值。执行 SELECT @@GLOBAL.sql_mode; 就能看到完整差异。
为什么不能简单关掉 strict 模式?
禁用 STRICT_TRANS_TABLES 确实能让老 SQL “跑起来”,但代价是掩盖真实问题:
- 隐式截断:比如插入超长字符串,5.7 可能只警告,8.0 严格模式下直接报错;关 strict 后变成静默截断,数据被悄悄破坏
- 零日期入库:
'0000-00-00'在 8.0 默认被拒,关 strict 后写入成功,但后续 JSON 解析、时间计算、ORM 映射都可能崩 - GROUP BY 不确定性:关
ONLY_FULL_GROUP_BY后,SELECT a, b FROM t GROUP BY a可能返回任意一条b值,结果不可复现
my.cnf 配置 sql_mode 不生效的常见原因
不是配置没写对,而是加载顺序或权限限制在捣鬼:
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 必须写在
[mysqld]段下,写在[client]或[mysql]下完全无效 - 云数据库(如阿里云 RDS、腾讯云 CDB)会强制重置
@@global.sql_mode,即使你改了配置文件也没用 - MySQL 启动时按顺序读多个配置文件(/etc/my.cnf → /etc/mysql/my.cnf → ~/.my.cnf),后加载的会覆盖前面的,用
mysqld --verbose --help | grep "Default options"查实际路径 - 某些 Docker 镜像或编译安装版本自带默认
sql_mode,会覆盖你的配置
应用层适配比服务端降级更可靠
真正稳定的解法不是让数据库迁就旧代码,而是分层处理:
- 连接串里加
?sessionVariables=sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'(JDBC 8.0+ 支持),只影响当前连接,不影响全局策略 - ORM 层统一拦截非空字段未赋值的 INSERT,补上
DEFAULT CURRENT_TIMESTAMP或显式传参 - 对历史遗留的零日期字段,用
ALTER TABLE t MODIFY c DATETIME NULL DEFAULT NULL;改结构,再批量 UPDATE 清洗 - GROUP BY 查询必须确保 SELECT 列要么在 GROUP BY 中,要么套聚合函数——这是 SQL 标准,不是 MySQL 特性
最易被忽略的是跨环境一致性:测试库用自建 8.0,生产用云 RDS,两者 sql_mode 实际值可能完全不同,不校验就上线,等于埋雷。










