SELECT子句中不存在聚合函数时的GROUP BY行为

时间:2009-10-20 00:45:04

标签: sql mysql

我有一个表emp,其中包含以下结构和数据:

name   dept    salary
-----  -----   -----
Jack   a       2
Jill   a       1
Tom    b       2
Fred   b       1

当我执行以下SQL时:

SELECT * FROM emp GROUP BY dept

我得到以下结果:

name   dept    salary
-----  -----   -----
Jill   a       1
Fred   b       1

服务器在什么基础上决定返回Jill和Fred并排除Jack和Tom?

我在MySQL中运行此查询。

注1:我知道查询本身没有意义。我试图用'GROUP BY'方案调试问题。我试图理解为此目的的默认行为。

注2:我习惯于编写与GROUP BY子句相同的SELECT子句(减去聚合字段)。当我遇到上述行为时,我开始想知道我是否可以依赖于以下场景:    选择emp表中的行,其中薪水是dept中最低/最高的。 例如:像这样的SQL语句适用于MySQL:

SELECT A.*, MIN(A.salary) AS min_salary FROM emp AS A GROUP BY A.dept

我没有找到任何描述这种SQL工作原理的材料,更重要的是如果我可以始终如一地依赖这种行为。如果这是一个可靠的行为,那么我可以避免像:

这样的查询
SELECT A.* FROM emp AS A WHERE A.salary = ( 
            SELECT MAX(B.salary) FROM emp B WHERE B.dept = A.dept)

9 个答案:

答案 0 :(得分:35)

在这一点上阅读MySQL documentation

简而言之,出于性能目的,MySQL允许省略GROUP BY中的某些列,但如果省略的列都具有相同的值 ,则仅适用于分组),否则,查询返回的值确实是不确定的,正如本文中其他人正确猜到的那样。确保添加ORDER BY子句不会重新引入任何形式的确定性行为。

虽然不是问题的核心,但这个例子显示了如何使用*而不是显式枚举所需列通常是一个坏主意。

摘自MySQL 5.0文档:

When using this feature, all rows in each group should have the same values
for the columns that are omitted from the GROUP BY part. The server is free
to return any value from the group, so the results are indeterminate unless
all values are the same. 

答案 1 :(得分:10)

这有点晚了,但我会把它放在以后参考。

GROUP BY获取具有重复项的第一行,并丢弃在结果集中匹配的任何行。因此,如果Jack和Tom拥有相同的部门,那么在普通SELECT中首先出现的人将成为GROUP BY中的结果行。

如果要控制列表中首先显示的内容,则需要执行ORDER BY。但是,SQL不允许ORDER BY在GROUP BY之前,因为它会抛出异常。此问题的最佳解决方法是在子查询中执行ORDER BY,然后在外部查询中执行GROUP BY。这是一个例子:

SELECT * FROM (SELECT * FROM emp ORDER BY name) as foo GROUP BY dept

这是我发现的最佳表现技巧。我希望这可以帮助别人。

答案 2 :(得分:4)

据我所知,为了您的目的,返回的特定行可以被认为是随机的。

排序仅在GROUP BY完成后进行

答案 3 :(得分:2)

您可以输入:

SET sql_mode ='ONLY_FULL_GROUP_BY'

在查询之前强制执行SQL标准GROUP BY行为

答案 4 :(得分:1)

我发现最好的办法是考虑不支持这种类型的查询。在大多数其他数据库系统中,您不能包含既不在GROUP BY子句中,也不能包含在HAVING,SELECT或ORDER BY子句中的聚合函数中的列。

相反,请考虑您的查询为:

SELECT ANY(name), dept, ANY(salary)
FROM emp 
GROUP BY dept;

......因为这是正在发生的事情。

希望这会有所帮助......

答案 5 :(得分:0)

我认为ANSI SQL要求select仅包含GROUP BY子句中的字段以及聚合函数。 MySQL的这种行为看起来像返回一些行,可能是服务器读取的最后一行,或者它手头的任何行,但不依赖于它。

答案 6 :(得分:0)

这将为每个人选择最近的一行:

SELECT * FROM emp
WHERE ID IN
(
    SELECT
        MAX(ID) AS ID
    FROM
        emp
    GROUP BY
        name
)

答案 7 :(得分:-1)

尝试使用ORDER BY选择所需的行。

SELECT * FROM emp GROUP BY dept ORDER BY name ASC;

将返回以下内容:

name   dept    salary
-----  -----   -----
jack   a       2
fred   b       1

答案 8 :(得分:-1)

如果按部门分组,其他数据是否重要?我知道Sql Server甚至不允许这个查询。如果有可能听起来可能存在其他问题。