Mysql5.7+的ONLY_FULL_GROUP_BY严格模式使用示例-分组需GROUP BY所有非聚合字段

一、严格模式的核心内涵

1. 模式组成

MySQL 5.7+默认启用的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

2. 设计目的

数据一致性:防止非确定性的查询结果

开发规范:强制编写符合SQL标准的语句

迁移兼容:向ANSI SQL标准靠拢

二、ONLY_FULL_GROUP_BY 规则详解

1. 核心约束(三维度验证)

2. 特殊场景处理

派生表:子查询中的GROUP BY不影响外层

JOIN操作:多表关联时需考虑所有表的字段

视图定义:视图创建时即检查模式合规性

 注:

博客:
https://blog.youkuaiyun.com/badao_liumang_qizhi

三、典型场景示例库

场景1:基础分组查询

-- 错误写法(缺少dept_name分组)
SELECT
    employee_id,
    dept_name,
    AVG(salary)
FROM employees
GROUP BY employee_id;
 
-- 正确写法
SELECT
    employee_id,
    dept_name,
    AVG(salary)
FROM employees
GROUP BY employee_id, dept_name;

场景2:多表关联查询

-- 错误写法(缺少orders.order_date )
SELECT
    c.customer_name,
    o.order_date,
    COUNT(*) as order_count
FROM customers c
JOIN orders o ON c.id  = o.customer_id
GROUP BY c.customer_name;
 
-- 正确写法
SELECT
    c.customer_name,
    o.order_date,
    COUNT(*) as order_count
FROM customers c
JOIN orders o ON c.id  = o.customer_id 
GROUP BY c.customer_name,  o.order_date;

场景3:函数依赖例外

-- 合法写法(name函数依赖于主键id)
SELECT
    id,
    name,
    MAX(login_time)
FROM users
GROUP BY id;  -- name字段自动关联

四、严格模式管理方案

1. 模式状态检查

-- 查看当前模式
SELECT @@sql_mode;
 
-- 会话级设置
SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
 
-- 全局设置(需重启)
SET GLOBAL sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES';

2. 生产环境建议

新项目:保持严格模式启用

旧系统迁移:分阶段实施

graph LR
  A[测试环境关闭严格模式] --> B[代码改造]
  B --> C[预发布环境验证]
  C --> D[生产环境逐步启用]

五、业务使用示例

  <select id="pageAlarmEvent" resultMap="PageResultMap">
    select
    ae.id,
    ae.event_name,
    ae.rule_name,
    ae.monitoring,
    ae.warehouse_id,
    ae.level,
    ar.time_interval,
    GROUP_CONCAT(an.title ORDER BY aen.id SEPARATOR ', ') AS notificationIdList,
    ae.notification_time,
    ae.start_time,
    ae.end_time,
    ae.is_next,
    ae.event_status,
    ae.update_user_id,
    ae.update_time
    FROM
    alarm_event ae
    LEFT JOIN
    alarm_rule ar ON ae.rule_id = ar.id
    LEFT JOIN
    alarm_event_notification aen ON ae.id = aen.event_id and aen.is_delete = 0
    LEFT JOIN
    alarm_notification an ON aen.notification_id = an.id
    <where>
      ae.is_delete = 0
      AND ae.company_id=#{companyId}
      <!-- 添加筛选条件 -->
      <if test="queryParam.warehouseId != null and queryParam.warehouseId != ''">
        AND ae.warehouse_id  = #{queryParam.warehouseId}
      </if>
      <if test="queryParam.tankId != null">
        AND ae.tank_id  = #{queryParam.tankId}
      </if>
      <if test="queryParam.level != null">
        AND ae.level  = #{queryParam.level}
      </if>
      <if test="queryParam.eventName != null and queryParam.eventName != ''">
        AND ae.event_name  LIKE CONCAT('%', #{queryParam.eventName}, '%')
      </if>
      <if test="queryParam.eventStatus != null">
        AND ae.event_status  = #{queryParam.eventStatus}
      </if>
    </where>
    GROUP BY
    ae.id,
    ae.event_name,
    ae.rule_name,
    ae.monitoring,
    ae.level,
    ae.warehouse_id,
    ar.time_interval,
    ae.notification_time,
    ae.start_time,
    ae.end_time,
    ae.is_next,
    ae.event_status,
    ae.update_user_id,
    ae.update_time
    <!-- 添加排序条件 -->
    ORDER BY ae.update_time  DESC
  </select>

### MySQL 5.7 中 `GROUP BY` 的正确使用方法 在 MySQL 5.7 及更高版本中,默认启用了 `sql_mode=only_full_group_by` 设置,这意味着任何不在 `GROUP BY` 子句中的列都必须作为聚合函数的一部分来处理。这确保了查询结果的一致性和可预测性。 #### 解决方案概述 为了使 SQL 查询符合 `ONLY_FULL_GROUP_BY` 模式的严格要求,在构建涉及 `GROUP BY` 的查询时注意以下几点: - **目标列表(Target List)**:即 `SELECT` 后面跟随的字段应要么出现在 `GROUP BY` 子句中,要么通过聚合函数计算得出。 - **分组依据(Group By Column)**:指定用于分组的标准,通常是一个或多个表内的唯一键或其他具有区分度高的属性。 下面给出几个具体的例子说明如何调整原有不符合新标准的SQL语句使其合法化并返回预期的数据集。 #### 示例代码展示 假设有一个名为 `t_user_role` 表结构如下所示: | ID | UserName | RoleName | IsSystem | |--|----------|----------| | 1 | Alice | Admin | true | | 2 | Bob | User | false | | 3 | Charlie | Guest | false | ##### 错误示范 原始尝试可能像这样写入,但在开启 `ONLY_FULL_GROUP_BY` 下会失败: ```sql SELECT ID, IsSystem FROM ( SELECT * FROM t_user_role ORDER BY id DESC ) AS t GROUP BY IsSystem; ``` 此命令试图先按降序排列记录再基于 `IsSystem` 列进行分组操作,但由于未满足上述提到的新规而导致错误发生[^1]。 ##### 正确做法之一 - 使用子查询与窗口函数 可以采用更复杂的逻辑实现相同目的而不违反规则: ```sql WITH RankedUsers AS( SELECT *, ROW_NUMBER() OVER(PARTITION BY IsSystem ORDER BY ID DESC) rn FROM t_user_role ) SELECT ID, IsSystem FROM RankedUsers WHERE rn = 1; ``` 这段脚本利用了一个公共表达式 (CTE),它首先按照 `IsSystem` 对数据分区,并给每一部分分配行号;接着只选取每一分区内最新加入成员的信息[^4]. ##### 方法二 - 修改 SQL_MODE 参数 另一种方式是在不影响业务逻辑的前提下临时关闭严格的 `GROUP BY` 验证机制。可以通过修改配置文件或者直接在当前连接上下文中更改参数值完成这一动作。不过要注意的是这种方法并不推荐长期应用因为可能会引入潜在风险影响数据库性能以及数据一致性等问题[^5]: ```sql SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY','')); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

霸道流氓气质

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值