sql_mode 是什么
sql_mode 是 MySQL 服务端的一组 SQL 行为开关,决定数据校验收多严、GROUP BY 是否允许歧义、双引号是字符串还是标识符。它解释的是「同一条 SQL 在不同实例上行为不同」这类问题:严格模式直接报错,宽松模式静默截断。默认取值随版本变化,5.7 升 8.0 时差异尤其明显。
基本信息
| 适用 | MySQL 5.7 / 8.0,MariaDB 项名相近但默认值不同 |
|---|---|
| 查看 | SELECT @@sql_mode; 与 SELECT @@GLOBAL.sql_mode; |
| 作用域 | SESSION 仅当前连接,GLOBAL 对新连接生效 |
| 持久化 | 写 my.cnf 的 [mysqld] 段,重启后仍生效 |
高频模式项含义
| 模式项 | 开启后的效果 | 关闭后的风险 |
|---|---|---|
| STRICT_TRANS_TABLES | 事务表写入非法值直接报错 | 超长或类型不符的数据被静默截断 |
| ONLY_FULL_GROUP_BY | 查询列表与分组必须语义确定 | 分组查询随机返回某一行的值 |
| NO_ZERO_DATE | 拒绝 0000-00-00 这类日期 | 零值日期继续写入,报表与统计出错 |
| ERROR_FOR_DIVISION_BY_ZERO | 严格模式下除零报错 | 除零结果静默变成 NULL |
| ANSI_QUOTES | 双引号变成标识符引用 | 老代码双引号字符串全部报 1054 |
| NO_ENGINE_SUBSTITUTION | 指定引擎不可用时报错 | 静默替换成默认引擎,表属性不符预期 |
查看与调整
查看与调整
SELECT @@sql_mode; # 当前会话实际取值
SELECT @@GLOBAL.sql_mode; # 全局默认值,新连接继承它
SET SESSION sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'; # 只影响本会话
SET GLOBAL sql_mode='STRICT_TRANS_TABLES,ONLY_FULL_GROUP_BY,NO_ZERO_DATE'; # 新连接生效
SHOW VARIABLES LIKE 'sql_mode'; # 与 @@sql_mode 交叉验证
-- my.cnf 持久化:[mysqld] 下写 sql_mode=...,重启后才是最终值典型使用场景
- 迁移前 diff 源库与目标库的 sql_mode,预判哪些 SQL 会因严格程度变化报错
- 老系统升级 8.0 后分组查询大面积报错,确认是否 ONLY_FULL_GROUP_BY 引起
- 数据修复窗口临时放宽会话级模式,避免修复语句被严格校验挡住
- 排查「写入成功但值不对」,回看是否发生静默截断或隐式取零
- 统一引号语义,减少跨库迁移时的语法差异
5.7 到 8.0 的默认值变化集中在新增 ONLY_FULL_GROUP_BY、移除 NO_AUTO_CREATE_USER、默认启用严格模式,逐项比对而不是整串照搬。
生产不要为了兼容老代码关闭严格模式:问题不会消失,只是被推迟到查询或对账时才暴露,届时修复数据的成本更高。