前言
最近公司开发同学A在群聊中说某某系统的某条SQL有问题,抛出异常,而同样的SQL在开发同学B(SQL由B同学编写)的环境没有问题,带着好奇心我上前协助一看,最终发现异常大致内容是:sql_mode=ONLY_FULL_GROUP_BY
,SQL大致类似如下格式:
SELECT name, age, SUM(sales) FROM orders GROUP BY name;
此时让我想起了年轻的自己,当初也犯过如此问题,于是我耐心的告知开发同学B:
- SELECT 中的非聚合字段必须出现在 GROUP BY 子句中
- 所有非聚合字段需通过聚合函数(如MAX、MIN、SUM)处理,或显式声明分组依据
解释完毕后,开发同学B提出疑问,为什么开发同学A的环境不行,而我的就可以,于是我在他们电脑分别使用了SELECT VERSION();
语句进行查看版本,然后告知开发同学A和开发同学B,自MySQL 5.7.5 起:官方将ONLY_FULL_GROUP_BY 加入默认的sql_mode,以增强 SQL 的标准兼容性和数据一致性,当我说完 打算扬长而去时,两位开发同学随即又抛出疑问:sql_mode是什么?我:......
简介
MySQL为了支持在不同的环境下运行,允许我们给它设置不同的运行模式(sql_mode)
sql_mode是MySQL中用于设置sql语法和行为的系统变量
控制MySQL的sql解析和执行的方式,使其与sql标准或其他数据库系统的行为一致,通过设置sql_mode,可以改变MySQL处理待定sql操作的方式
我们在安装mysql时就要确定它的运行模式(sql_mode),不建议在中途更改它的运行模式
建议在跨mysql实例备份还原数据时,保持双方mysql实例具有相同的运行模式
sql_mode模式
MySQL 支持的 sql_mode
模式
模式名 | 含义 |
---|---|
STRICT_TRANS_TABLES | 事务表中对无效数据报错,非事务表警告 |
STRICT_ALL_TABLES | 所有表对无效数据都报错 |
NO_ZERO_IN_DATE | 不允许日期中的月或日为 0(如 2023-00-10) |
NO_ZERO_DATE | 不允许日期为 '0000-00-00' |
ERROR_FOR_DIVISION_BY_ZERO | 除以 0 报错 |
NO_AUTO_CREATE_USER | 禁止 GRANT 自动创建用户(MySQL 8.0 移除) |
NO_ENGINE_SUBSTITUTION | 禁止在指定引擎不可用时自动替代 |
ONLY_FULL_GROUP_BY | GROUP BY 中必须列出所有非聚合列 |
ANSI_QUOTES | 用双引号表示标识符(字段、表名等) |
PIPES_AS_CONCAT | 使用 “||” 表示字符串拼接 |
IGNORE_SPACE | 函数名后允许加空格 |
ANSI | 启用符合 ANSI SQL 的语法和行为 |
TRADITIONAL | 启用接近标准 SQL 的严格模式组合 |
NO_BACKSLASH_ESCAPES | 禁用反斜杠 “\” 作为转义字符 |
HIGH_NOT_PRECEDENCE | 改变 “NOT” 运算符的优先级 |
NO_UNSIGNED_SUBTRACTION | 无符号数减法结果转换为有符号类型 |
REAL_AS_FLOAT | 将 “REAL” 类型作为 “FLOAT” 处理 |
PAD_CHAR_TO_FULL_LENGTH | 使用 “CHAR” 比较时右侧填充空格至固定长度 |
ALLOW_INVALID_DATES | 允许插入无效日期(非严格模式时有效) |
TIME_TRUNCATE_FRACTIONAL | 时间字段截断小数秒部分而非四舍五入 |
sql_mode对比
对比项 | MySQL 5.7 默认值 | MySQL 8.0 默认值 |
---|---|---|
默认 sql_mode | ONLY_FULL_GROUP_BY, STRICT_TRANS_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER, NO_ENGINE_SUBSTITUTION | ONLY_FULL_GROUP_BY, STRICT_TRANS_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_ENGINE_SUBSTITUTION |
NO_AUTO_CREATE_USER | 默认启用 | 已移除(不再支持 GRANT 创建用户) |
严格性 | 中等 | 更严格,接近 SQL 标准 |
查看和修改sql_mode
sql_mode,分为全局的、会话的(即:针对当前链接有效),在mysql运行时查看和更改sql_mode的sql如下:
-- 查看当前会话的sql_mode
select @@sql_mode
SELECT @@SESSION.sql_mode;
-- 查看全局的sql_mode
SELECT @@GLOBAL.sql_mode;
-- 设置当前会话的sql_mode
set sql_mode="TRADITIONAL";
SET SESSION sql_mode = 'TRADITIONAL';
-- 设置全局的sql_mode
SET GLOBAL sql_mode = 'TRADITIONAL';
-- 添加 sql_mode
SET SESSION sql_mode = sys.list_add(@@session.sql_mode, 'ONLY_FULL_GROUP_BY');
SET GLOBAL sql_mode = sys.list_add(@@global.sql_mode, 'ONLY_FULL_GROUP_BY');
不过,上述的修改当myql服务器重启时就自动失效了,为了长久有效,我们可以在my.cnf
或 my.ini
文件中进行配置