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

Ran*_*Guy 240 sql group-by aggregate-functions postgresql-9.1

我有一张看起来像这个来电者'makerar'的桌子

 cname  | wmname |          avg           
--------+-------------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  | 1.00000000000000000000
 spain  | usopp  |     5.0000000000000000
Run Code Online (Sandbox Code Playgroud)

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

SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname;
Run Code Online (Sandbox Code Playgroud)

但我会收到一个错误,

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;
Run Code Online (Sandbox Code Playgroud)

所以我这样做

SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname, wmname;
Run Code Online (Sandbox Code Playgroud)

但这不会给出意图的结果,并显示下面的错误输出.

 cname  | wmname |          max           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  | 1.00000000000000000000
 spain  | usopp  |     5.0000000000000000
Run Code Online (Sandbox Code Playgroud)

实际结果应该是

 cname  | wmname |          max           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000
Run Code Online (Sandbox Code Playgroud)

我该如何解决这个问题呢?

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

Seb*_*bas 205

是的,这是一个常见的聚合问题.在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
Run Code Online (Sandbox Code Playgroud)

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

SELECT cname, wmname, MAX(avg) OVER (PARTITION BY cname) AS mx
FROM makerar
;
Run Code Online (Sandbox Code Playgroud)

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

 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  |     5.0000000000000000
 spain  | usopp  |     5.0000000000000000
Run Code Online (Sandbox Code Playgroud)

显示唯一(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
Run Code Online (Sandbox Code Playgroud)

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

  • 在 MySQL 5.7 中关闭 `ONLY_FULL_GROUP_BY` 不会激活 SQL 标准指定何时可以从 `group by` 中省略列的方式(或使 MySQL 表现得像 Postgres)。它只是恢复到 MySQL 返回随机(=“不确定”)结果的旧行为。 (2认同)

ype*_*eᵀᴹ 110

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

SELECT DISTINCT ON (cname) 
    cname, wmname, avg
FROM 
    makerar 
ORDER BY 
    cname, avg DESC ;
Run Code Online (Sandbox Code Playgroud)

  • 如果想要对像avg这样的列进行排序,它将无法正常工作 (4认同)
  • 当然.如果你没有运行我发布的查询,你会得到不同的结果!这与"它不会按预期工作"不一样...... (3认同)

小智 20

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

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

SELECT cname, (array_agg(wmname ORDER BY avg DESC))[1], MAX(avg)
FROM makerar GROUP BY cname;
Run Code Online (Sandbox Code Playgroud)

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


zer*_*323 15

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;
Run Code Online (Sandbox Code Playgroud)

使用rank() 窗口功能:

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

注意

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


ox1*_*05d 8

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

SELECT cname, MAX(avg) FROM makerar GROUP BY cname;
Run Code Online (Sandbox Code Playgroud)

结果将是:

 cname  |      MAX(avg)
--------+---------------------
 canada | 2.0000000000000000
 spain  | 5.0000000000000000
Run Code Online (Sandbox Code Playgroud)

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

  cname | wmname |        avg           
--------+--------+-----------------------
 spain  | zoro   |  1.0000000000000000
 spain  | luffy  |  5.0000000000000000
 spain  | usopp  |  5.0000000000000000
Run Code Online (Sandbox Code Playgroud)

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

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