MySQL 8.0 默认启用 STRICT_TRANS_TABLES 和 ONLY_FULL_GROUP_BY,导致 INSERT 缺失非空字段值或 GROUP BY 查询不合规时报错;应补全字段、重写查询或调整表结构,而非关闭严格模式。

MySQL 5.7 升级到 8.0 后 INSERT 报错 Field 'xxx' doesn't have a default value
这是最典型的严格模式触发问题:MySQL 8.0 默认启用 STRICT_TRANS_TABLES,而旧版本可能关闭或仅部分启用。当表中某列定义为 NOT NULL 且无默认值,但 INSERT 语句又没显式提供该列值时,8.0 直接拒绝写入。
实操建议:
- 先查当前模式:
SELECT @@sql_mode;,重点看是否含STRICT_TRANS_TABLES或STRICT_ALL_TABLES - 临时绕过(仅调试):
SET SESSION sql_mode = '';,但不解决根本问题 - 长期方案不是关严格模式,而是补全缺失字段——检查应用层
INSERT语句,确保所有NOT NULL列都有值或明确设为DEFAULT - 若字段确实允许空,改表结构:
ALTER TABLE t MODIFY col VARCHAR(20) NULL;,而非降级 SQL 模式
为什么 GROUP BY 在 MySQL 8.0 报错 Expression #1 of SELECT list is not in GROUP BY clause
这是因为 ONLY_FULL_GROUP_BY 在 8.0 成为默认模式之一。它强制要求 SELECT 列必须是 GROUP BY 中的列、聚合函数结果,或功能依赖于分组列的表达式。
常见错误场景:用 SELECT id, name, COUNT(*) FROM user GROUP BY dept_id; ——id 和 name 不在 GROUP BY 中,也不被聚合,MySQL 无法确定返回哪一行的值。
实操建议:
- 别直接删
ONLY_FULL_GROUP_BY:它能暴露查询逻辑缺陷,关掉反而掩盖数据歧义 - 重写查询:要么把
id、name加进GROUP BY,要么用聚合函数包裹,如MAX(name) - 若业务真需要“每组任取一行”,用窗口函数更清晰:
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY id) rn+ 外层过滤rn = 1 - 确认兼容性:低版本 MySQL 可能不支持窗口函数,需评估客户端 MySQL 驱动和服务器版本匹配
sql_mode 配置写在 my.cnf 里却没生效?
配置项加载顺序和作用域容易混淆:全局配置文件(/etc/my.cnf 或 /usr/etc/my.cnf)只影响服务启动时的默认值;已运行实例需手动刷新,且用户会话可覆盖全局设置。
实操建议:
- 检查实际生效值用
SELECT @@GLOBAL.sql_mode;和SELECT @@SESSION.sql_mode;,二者可能不同 - 修改
my.cnf后必须重启 MySQL 服务,SET GLOBAL仅临时生效,重启即丢失 - 避免混用:不要在
my.cnf里写sql_mode=STRICT_TRANS_TABLES,NO_ZERO_DATE,又在应用连接后执行SET SESSION sql_mode = '',后者会完全覆盖前者 - 生产环境推荐最小化模式:保留
STRICT_TRANS_TABLES和ONLY_FULL_GROUP_BY,去掉过时的NO_AUTO_CREATE_USER(8.0.11+ 已废弃)
PHP/Python 应用连上 MySQL 8.0 后日期插入变 0000-00-00 或报错
这通常由 NO_ZERO_DATE 和 NO_ZERO_IN_DATE 导致。老应用常习惯传 '0000-00-00' 表示“无日期”,但新 MySQL 拒绝该值并抛异常,或静默转成 NULL(取决于其他模式组合)。
实操建议:
- 别依赖
0000-00-00:它是非标准值,PostgreSQL / SQL Server 均不支持,应统一用NULL - 检查 ORM 配置:Laravel 的
$dates、Django 的blank=True, null=True是否正确设置;SQLAlchemy 是否启用了default=None - 如果必须兼容旧数据,可在建表时显式允许零日期:
CREATE TABLE ... date_col DATE NULL DEFAULT NULL,再配合应用层校验 - 注意时区交互:PHP 的
PDO::ATTR_EMULATE_PREPARES = false下,时间字符串解析受 MySQL 服务端time_zone影响,和sql_mode无关但常被误判
真正麻烦的不是模式开关本身,而是那些藏在 ORM 自动生成 SQL 里的隐式假设——比如认为 NOT NULL 字段总能靠数据库默认值兜底,或相信 GROUP BY 会稳定返回第一行。这些在宽松模式下跑得欢,一开严格模式就露馅。










