GROUP BY字段的序列影响MySQL查询结果

时间:2016-01-06 19:38:49

标签: mysql group-by

我正在学习MySQL并在有练习题的网站上进行练习(未命名,但问题是81)。问题是GROUP BY之后的字段序列。我确定GROUP BY后面的字段序列会根据报告的隐藏表中预期行数和实际行数之间的比较来影响查询结果。我对本网站及其他网站的大量阅读理解是不应该的。

任务是:

 From Outcome table, retrieve all rows for that month (months) 
 in view of a year, in which total value of expenses (out) is maximal.

表格描述为:

 Outcome(code, point, date, out) where code is the primary key, 
 point is a simple integer, date is in the format datetime, and out is a currency value.

这是我的问题:

SELECT code,point,date,`out` outc FROM outcome
    WHERE EXTRACT(MONTH FROM date) =
        (SELECT mon bestmonth FROM 
            (SELECT MAX(sout), mon,yr FROM
                (SELECT SUM(outc) sout,mon,yr FROM
                    (SELECT EXTRACT(MONTH FROM date) mon, `out` outc,
                         date,EXTRACT(YEAR FROM date) yr FROM outcome
                    ) maxmonth GROUP BY mon,yr
                ) peak
            ) tmonth
        ) 
    AND EXTRACT(YEAR FROM date) =
        (SELECT yr bestyear FROM 
            (SELECT MAX(yout), mon,yr FROM
                (SELECT SUM(outy) yout,mon,yr FROM
                    (SELECT EXTRACT(MONTH FROM date) mon, `out` outy,
                         date,EXTRACT(YEAR FROM date) yr FROM outcome
                    ) maxyear GROUP BY yr,mon
                ) peakb
            ) tyear
        )

虽然不是一个优雅的查询,但我想了解为什么要改变订单来自' GROUP BY mon,yr'到了' GROUP BY你,周一'在maxmonth和maxyear子查询中有任何影响。

在maxmonth子查询中,' GROUP BY mon,yr'结果:

Wrong  Your query produced correct result set on main database, 
but it failed test on second, checking database
* Wrong number of records (less by 6)

对于相同的maxmonth子查询,' GROUP BY yr,mon'结果:

Wrong  Your query produced correct result set on main database, 
but it failed test on second, checking database
* Wrong number of records (less by 11)

3 个答案:

答案 0 :(得分:0)

您需要了解按1列分组,并不意味着所有其他列都具有分组值的值。

例如,给出表格:

 customer | value |  date
       1  |   2   | 2015-01-03
       1  |   3   | 2015-01-05
       2  |   3   | 2015-01-02
       2  |   4   | 2015-01-03
       2  |   5   | 2015-01-04

如果您使用

select customer, max(value), date from table group by customer

您的结果可能是

customer | max(value) |  date
      1  |      3     | 2015-01-03
      2  |      5     | 2015-01-02

它不是你想要的......因为agregation函数只适用于THAT列。

它可以提供帮助:

select year(date) yr, month(date) mon, sum(outc) totalOfMonth from outcome group by yr, mon order by totalOfMonth

答案 1 :(得分:0)

GROUP BY目前影响返回结果的顺序(虽然MySQL警告说这种行为将来可能会改变,所以不要依赖它)。由于包含这些GROUP BY查询的查询隐含了不包含非聚合字段的GROUP BY子句,因此选择为这些字段返回的值是官方不确定的。

(通常是遇到的第一个或最后一个值,我因为它们的不可预测性而避免这样的查询)。

编辑/仅供参考:大多数其他RDBMS甚至不允许使用不包含所有非聚合字段的GROUP BY子句进行查询。 MySQL甚至允许它受到批评;虽然我的假设是原始意图(以及更新版本的服务器设置似乎证实了这一点)是为了允许更简洁的查询,其中查询作者知道每个GROUPed组的非聚合字段只有一组值值;例如,当一个表上的主键分组而不包括可能变化的连接表中的字段时(例如:只有非聚合的,未分组的字段来自其PK是分组条件的一部分的表)。

答案 2 :(得分:0)

基本上,我对答案的解释是在使用GROUP BY时始终使用所有相关的非聚合字段。如果所涉及的表中存在其他字段,则可能存在问题,并且在这种情况下,MAX值可能不是真MAX值并且可以是任意的。原始查询在结果表(maxmonth派生表)中包含GROUP BY,其中不包括所有非聚合字段,并且GROUP BY的结果是可疑的。

据我所知,答案的要点是:

1)对于表格具有聚合值(如MAX)的查询,应报告与GROUP BY子句(客户)中包含的字段值对应的实际MAX值,但不包括在GROUP BY中的字段(例如日期) )不一定对应于正确的MAX值。从上面的第一个答案中可以看出,客户和MAX值是正确的,但日期(不包括在GROUP BY中)可能实际上不对应于customer / MAX值行。使用仅具有相关非聚合值(月和年)的派生表(formattedOutMonthYear_sq表)并创建新的派生表(groupedOutMonthYear_sq表)来执行聚合SUM应该导致月份和年份的正确值,尽管代码和点如果使用结果表而不是formattedOutMonthYear表,则字段可能不会。

2)使用诸如MAX(峰值表)之类的聚合而不明确地包括非聚合字段的任何GROUP BY可能导致意外结果。在原始代码中,由于隐式分组,派生表上的聚合MAX(包括GROUP BY子句中的非聚合)仍然可以聚类。

GROUP BY仍然包含多个列,但我对上述答案的解释是,如果包含所有非聚合列(Uueerdo)并且不从查询(Renan)推断出其他字段,则可以接受多个列。

不幸的是,运行查询会导致运行时错误,这是不幸的。感谢您解释我观察到的结果,并将此逻辑纳入查询编写中。

SELECT o.code cd,o.point pnt ,o.date dt,`out` expense,mdt FROM outcome o
JOIN 
(SELECT EXTRACT(MONTH FROM date) mdt, EXTRACT(YEAR FROM date) ydt, code FROM outcome
) mnth
ON mnth.code = o.code
WHERE mdt =
    (
    SELECT distinct mon topMonth from 
        (SELECT SUM(outm) allOutMonth, mon, yr FROM 
            (SELECT EXTRACT(MONTH FROM date) mon, `out` outm, EXTRACT(YEAR FROM date) yr 
            FROM outcome
            ) formattedOutMonthYear_sq GROUP BY yr, mon
        ) topMonth_sq 
        WHERE topMonth_sq.allOutMonth >= all
        (SELECT allOutMonth from
            (SELECT SUM(outm) allOutMonth, mon, yr FROM 
                (SELECT EXTRACT(MONTH FROM date) mon, `out` outm,
                    EXTRACT(YEAR FROM date) yr 
                FROM outcome
                ) formattedOutMonthYear_sq GROUP BY yr, mon 
            ) groupedOutMonthYear_sq 
        )
    )        
AND ydt =
    (
    SELECT yr topTopYear from 
        (SELECT SUM(outm) allOutYear, mon, yr FROM 
            (SELECT EXTRACT(MONTH FROM date) mon, `out` outm, 
                EXTRACT(YEAR FROM date) yr 
            FROM outcome
            ) formattedOutYearMonth_sq GROUP BY yr, mon
        ) topYear_sq 
        WHERE topYear_sq.allOutYear >= all
        (SELECT allOutYear from
            (SELECT SUM(outm) allOutYear, mon, yr FROM 
                (SELECT EXTRACT(MONTH FROM date) mon, 
                    `out` outm, EXTRACT(YEAR FROM date) yr 
                FROM outcome
                ) formattedOutMonthYear_sq GROUP BY yr, mon 
            ) groupedOutYearMonth_sq 
        )
    )