必须出现在GROUP BY子句中或用于聚合函数

时间:2013-10-26 01:50:26

标签: sql group-by aggregate-functions postgresql-9.1

我有一个看起来像这个来电者'makerar'的表

 cname  | wmname |          avg           
--------+-------------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  | 1.00000000000000000000
 spain  | usopp  |     5.0000000000000000

我想为每个cname选择最大平均值。

SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname;

但我会收到错误,

ERROR:  column "makerar.wmname" must appear in the GROUP BY clause or be used in an   aggregate function 
LINE 1: SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname;

所以我这样做

SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname, wmname;

然而,这不会给出意图的结果,并显示下面的错误输出。

 cname  | wmname |          max           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  | 1.00000000000000000000
 spain  | usopp  |     5.0000000000000000

实际结果应为

 cname  | wmname |          max           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

我该如何解决此问题?

注意:此表是从先前操作创建的VIEW。

7 个答案:

答案 0 :(得分:175)

是的,这是一个常见的聚合问题。在SQL3 (1999)之前,所选字段必须出现在GROUP BY子句[*]中。

要解决此问题,您必须在子查询中计算聚合,然后将其与自身连接以获取您需要显示的其他列:

SELECT m.cname, m.wmname, t.mx
FROM (
    SELECT cname, MAX(avg) AS mx
    FROM makerar
    GROUP BY cname
    ) t JOIN makerar m ON m.cname = t.cname AND t.mx = m.avg
;

 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

但你也可以使用看起来更简单的窗口函数:

SELECT cname, wmname, MAX(avg) OVER (PARTITION BY cname) AS mx
FROM makerar
;

这种方法的唯一之处在于它将显示所有记录(窗口函数不分组)。但它会显示每行中国家/地区的正确值(即cname级别)MAX,因此由您决定:

 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  |     5.0000000000000000
 spain  | usopp  |     5.0000000000000000

显示唯一匹配最大值的(cname, wmname)元组的解决方案,可称不太优雅,是:

SELECT DISTINCT /* distinct here matters, because maybe there are various tuples for the same max value */
    m.cname, m.wmname, t.avg AS mx
FROM (
    SELECT cname, wmname, avg, ROW_NUMBER() OVER (PARTITION BY avg DESC) AS rn 
    FROM makerar
) t JOIN makerar m ON m.cname = t.cname AND m.wmname = t.wmname AND t.rn = 1
;


 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

[*]:有趣的是,即使规范类型允许选择非分组字段,主要引擎似乎也不喜欢它。 Oracle和SQLServer根本不允许这样做。默认情况下Mysql曾经允许使用它,但是现在自5.7以来管理员需要在服务器配置中手动启用此选项(ONLY_FULL_GROUP_BY)以支持此功能...

答案 1 :(得分:99)

在Postgres中,您还可以使用特殊的 DISTINCT ON (expression) 语法:

SELECT DISTINCT ON (cname) 
    cname, wmname, avg
FROM 
    makerar 
ORDER BY 
    cname, avg DESC ;

答案 2 :(得分:17)

group by选择中指定非分组和非聚合字段的问题是引擎无法知道在这种情况下它应返回哪个记录的字段。这是第一次吗?它持续了吗?通常没有与聚合结果自然对应的记录(minmax是例外)。

但是,有一种解决方法:也可以聚合必填字段。 在posgres中,这应该有效:

SELECT cname, (array_agg(wmname ORDER BY avg DESC))[1], MAX(avg)
FROM makerar GROUP BY cname;

请注意,这会创建一个由avg排序的所有wnames数组,并返回第一个元素(postgres中的数组是从1开始的。)

答案 3 :(得分:14)

SELECT t1.cname, t1.wmname, t2.max
FROM makerar t1 JOIN (
    SELECT cname, MAX(avg) max
    FROM makerar
    GROUP BY cname ) t2
ON t1.cname = t2.cname AND t1.avg = t2.max;

使用rank() window function

SELECT cname, wmname, avg
FROM (
    SELECT cname, wmname, avg, rank() 
    OVER (PARTITION BY cname ORDER BY avg DESC)
    FROM makerar) t
WHERE rank = 1;

注意

每个组都会保留多个最大值。如果你想要每组只有一个记录,即使有多个avg等于max的记录,你应该检查@ ypercube的答案。

答案 4 :(得分:4)

对我来说,这与“常见聚合问题”无关,而仅与错误的SQL查询有关。唯一正确的答案是“为每个用户名选择最大平均...”

SELECT cname, MAX(avg) FROM makerar GROUP BY cname;

结果将是:

 cname  |      MAX(avg)
--------+---------------------
 canada | 2.0000000000000000
 spain  | 5.0000000000000000

此结果通常回答问题“每个组的最佳结果是什么?” 。我们看到,西班牙的最佳结果是5,而加拿大的最佳结果是2。这是事实,没有错误。  如果还需要显示 wmname ,我们必须回答以下问题:“从结果集中选择wmname的规则是什么?”让我们稍微改变一下输入数据来澄清错误:

  cname | wmname |        avg           
--------+--------+-----------------------
 spain  | zoro   |  1.0000000000000000
 spain  | luffy  |  5.0000000000000000
 spain  | usopp  |  5.0000000000000000

您期望在运行查询SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;的哪个结果?是spain+luffy还是spain+usopp?为什么?在查询中不确定,如果几个合适,如何选择“更好”的 wmname ,因此结果也不确定。这就是为什么SQL解释器返回错误-查询不正确的原因。

换句话说,对于“谁在spain组中是最好的?”这个问题没有正确的答案。。路飞并不比usopp更好,因为usopp具有相同的“得分”。

答案 5 :(得分:0)

我最近在尝试使用case when计算时遇到此问题,并发现更改whichcount语句的顺序可以解决问题:

SELECT date(dateday) as pick_day,
COUNT(CASE WHEN (apples = 'TRUE' OR oranges 'TRUE') THEN fruit END)  AS fruit_counter

FROM pickings

GROUP BY 1

而不是使用 - 在后者中,我得到的错误是苹果和橙子应该出现在聚合函数中

CASE WHEN ((apples = 'TRUE' OR oranges 'TRUE') THEN COUNT(*) END) END AS fruit_counter

答案 6 :(得分:0)

这似乎也有效

function validateInput(x) {
    if (isNaN(x) || x < 0) {
        alert("Input incorrect; fields must be blank or contain positive integers");
        return false;
    }
    else { return true
    }
}