MySQL中的Group By是否允许SELECT非聚合列的总结

样例数据:

数据库表结构:

CREATE TABLE `student` (
  `sno` varchar(20) NOT NULL,
  `sname` varchar(20) DEFAULT NULL,
  `ssex` varchar(20) DEFAULT NULL,
  `sage` int(11) DEFAULT NULL,
  `sdept` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`sno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

数据库表数据:

INSERT INTO databasebook.student (sno, sname, ssex, sage, sdept) VALUES ('201215121', '李勇', '男', 20, 'CS');
INSERT INTO databasebook.student (sno, sname, ssex, sage, sdept) VALUES ('201215122', '刘晨', '女', 19, 'CS');
INSERT INTO databasebook.student (sno, sname, ssex, sage, sdept) VALUES ('201215123', '王敏', '女', 18, 'MA');
INSERT INTO databasebook.student (sno, sname, ssex, sage, sdept) VALUES ('201215124', '张立', '男', 19, 'IS');
INSERT INTO databasebook.student (sno, sname, ssex, sage, sdept) VALUES ('201215125', '张立', '男', 19, 'IS');

业务场景以及解决方案

业务场景:在业务开发时候遇见一场景需要根据某几个字段进行去重选择出所有出重之后的Row,在本文中可以认为是需要对ssex进行去重,在性别为男、性别为女的中各选择一条记录即可。于是在公司的测试数据库编写SQL为:

SELECT s.* from student s  GROUP BY s.ssex ;

查询结果:

snosnamessexsagesdept
201215121李勇20CS
201215122刘晨19CS

满足需要,但是随后切换到本地的MYSQL执行该SQL发现报错,错误信息如下:

[42000][1055] Expression #1 of SELECT list is not in GROUP BY clause and 
contains nonaggregated column 'databasebook.s.sno' which is not functionally
 dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

错误信息很明显,存在SELECT的列不在GROUP BY中,因此只能根据错误信息顺藤摸瓜了,寻找错误根源,MYSQL官网如下:MYSQL官方解释 如下:

SQL92以及更早的SQL标准中不允许查询除了GROUP BY之外的非聚合的列,例如如下查询即非法:

  SELECT o.custid, c.name, MAX(o.payment)
  FROM orders AS o, customers AS c
  WHERE o.custid = c.custid
  GROUP BY o.custid;

因为c.name没有在group中,因此解决方案是删除c.name或者将c.name添加到group by中。但是在后期的SQL99标准中便允许查询非聚合的列了。

 

在MYSQL的5.7.5以及以上版本默认的设置是:ONLY_FULL_GROUP_BY  ,该设置则约束查询必须是聚合的列。但是在其版本之前则允许查询非聚合的列。我本地MYSQL是:5.7.22-log,公司的版本是:5.5.50-MariaDB。更多细节参考:SQL_MODE 。如果ONLY_FULL_GROUP_BY 开启的但是还想查询非聚合的列可以使用ANY_VALUE(非聚合列)进行查询,ANY_VALUE参考文档。还有一种情况开启ONLY_FULL_GROUP_BY时,如果GROUP BY是主键或者 unique NOT NULL 时是可以查询非聚合的列的,原因是此时分组的key是主键,则每一个分组只有一条数据,因此是可以进行查询非聚合的列的。最后对于高于5.7.5的版本如果想查询非聚合的列可以关闭ONLY_FULL_GROUP_BY 属性,即:

set sql_mode ='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';

 

关于sql_mode系统变量说明

根据sql_mode的系统变量值MYSQL服务器可以工作在不同的SQL模式下,sql_mode主要影响的是sql的语法该变量可以使得MYSQL更容易与与其他DB Server一起协作。关于sql_mode的一切高频次问题参见:高频次问题答案

设置sql_mode的变量值方式

  1. 命令行参数:--sql-mode="modes" 
  2. 配置文件:my.cnf
  3. 运行时修改:SET GLOBAL sql_mode = 'modes';或者SET SESSION sql_mode = 'modes'; 作用域的差别而已。

查看sql_mode的变量值

SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;

sql_mode几个比较重要的值

参见:https://dev.mysql.com/doc/refman/5.6/en/sql-mode.html

 

<think>好的,我现在需要解释“SELECT 中的每个非聚合列都必须出现在 GROUP BY 子句中”这个SQL规则。首先,我得确定用户对这个概念的理解程度。可能用户在使用GROUP BY时遇到了错误,或者在学习聚合函数时碰到这个问题。 我应该从基本概念入手,比如什么是聚合函数,什么是GROUP BY子句。然后解释为什么非聚合列必须出现在GROUP BY里。可能需要用例子来说明,这样更直观。 接下来,要区分MySQL的宽松模式和其他数据库的严格模式,比如PostgreSQL。这里可能会让用户困惑,为什么有时候同样的SQL在不同数据库表现不同。需要指出MySQL在某些情况下允许非聚合列不在GROUP BY中,但结果可能不可靠,而其他数据库如PostgreSQL则严格执行这一规则。 然后,举几个例子,正确和错误的对比。例如,正确的例子包括所有非聚合列都在GROUP BY中,而错误的例子则展示如果遗漏某个会怎样。同时,给出解决方案,比如添加缺失的GROUP BY,或者使用聚合函数处理。 还要注意提到常见的聚合函数,如SUM、AVG、MAX等,以及它们在处理非聚合列时的作用。最后,强调遵循这一规则的重要性,以确保查询结果的可预测性和准确性。 可能需要检查是否有遗漏点,比如提到DISTINCT关键字是否会影响这个规则,或者窗口函数的情况,但根据问题范围,可能不需要深入这些细节。保持解释简洁,重点明确,帮助用户理解核心概念。</think>在 SQL 中,当使用 `GROUP BY` 子句时,有一条重要规则: **SELECT 中的每个非聚合列都必须出现在 GROUP BY 子句中** (即:所有未包裹在聚合函数中的,必须在 `GROUP BY` 后) --- ### 核心原理分步解释 1. **`GROUP BY` 的作用** `GROUP BY` 用于将数据按指定分组,并对每组数据进行聚合计算(如求和、平均等)。例如: ```sql SELECT department, AVG(salary) FROM employees GROUP BY department; ``` 这里按 `department` 分组,计算每个部门的平均工资。 2. **聚合函数与非聚合列** - **聚合函数**:如 `SUM()`, `AVG()`, `COUNT()`, `MAX()`, `MIN()` 等,它们对一组值进行计算并返回单个值。 - **非聚合列**:未包裹在聚合函数中的。例如上例中的 `department`。 3. **为什么需要此规则?** - **数据一致性**:如果非聚合列不在 `GROUP BY` 中,数据库无法确定该的值应如何从组内多行数据中选取。 - **避免歧义**:假设一个组内有多个不同的 `employee_name`,但未在 `GROUP BY` 中指定,此时返回哪个名字是未定义的。 --- ### 示例分析 #### 正确写法 ✅ ```sql SELECT department, job_role, AVG(salary) FROM employees GROUP BY department, job_role; ``` - `department` 和 `job_role` 是非聚合列,且都在 `GROUP BY` 中。 - `AVG(salary)` 是聚合。 #### 错误写法 ❌ ```sql SELECT department, job_role, AVG(salary) FROM employees GROUP BY department; ``` - `job_role` 是非聚合列,但未出现在 `GROUP BY` 中。 - 数据库会报错:`Column 'employees.job_role' must appear in the GROUP BY clause...` --- ### 例外情况 1. **MySQL 的宽松模式** MySQL 在某些配置下允许非聚合列不在 `GROUP BY` 中,但会随机返回组内某一行的值,可能导致不可预测的结果。 2. **函数依赖(高级特性)** 部分数据库(如 PostgreSQL)支持函数依赖推断。例如,如果 `employee_id` 是主键且出现在 `GROUP BY` 中,其他(如 `employee_name`)可能被允许不参与分组。 --- ### 解决方案 1. **将非聚合列添加到 `GROUP BY`** ```sql SELECT department, job_role, AVG(salary) FROM employees GROUP BY department, job_role; ``` 2. **使用聚合函数包裹非聚合列** ```sql SELECT department, MAX(job_role) AS example_role, AVG(salary) FROM employees GROUP BY department; ``` --- ### 总结 这条规则本质上是为了保证查询结果的确定性。通过强制所有非聚合列参与分组,数据库能明确知道如何将原始数据映射到最终结果的行中。在编写 `GROUP BY` 查询时,始终检查 `SELECT` 是否符合此规则。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

javartisan

对您有帮助,欢迎老板赐一杯奶茶

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

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

打赏作者

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

抵扣说明:

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

余额充值