2013-10-26 114 views
128

使用我有一个表,看起来像这样呼叫者makerar“必须出现在GROUP BY子句或聚合函数

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; 

但是这不会给intented结果,以下不正确的输出显示。

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。

+2

相关:http://stackoverflow.com/q/18061285/398670 –

回答

114

是的,这是一个常见的聚合问题。在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

感谢语法是corect,但是,当您加入 – RandomGuy

+1

时,您必须比较mx和avg的值是您的语法正确并消除重复,但是您需要m .avg = t.mx(在你写JOING之后)以获得预期的结果 – RandomGuy

+1

@Sebas它可以在不加入MAX的情况下完成(参见@ypercube的回答,在我的答案中也有另一种解决方案),但不能你这样做的方式。检查预期的输出。 – zero323

10
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; 

注意

两者将保留每个组中的多个最大值。如果你只想每个组的单个记录,即使有多个记录的平均等于最大值,你应该检查@ ypercube的答案。

79

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

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

如果想排序像avg – amenzhinsky

+0

@amenzhinsky这样的列,它将无法正常工作你是什么意思?如果想要使用与“BY cname”不同的顺序排序结果集? –

+0

@ypercube,其实psql首先排序,然后应用DISTINCT。在通过avg排序的情况下,我们会根据排序方向得到每行最小值和最大值的不同结果 – amenzhinsky

9

group by选择指定不分组的和非聚集领域的问题是,发动机有没有办法知道,在这种情况下应该返回哪个记录的字段。它是第一个吗?它是最后?通常没有自然对应于汇总结果的记录(minmax是例外)。

但是,还有一种解决方法:将所需的字段也汇总。 在posgres,这应该工作:

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

注意,这创建所有wnames,通过平均有序的阵列,并返回第一个元件(在postgres的阵列是基于1的)。

+0

好点。尽管数据库似乎可以做一个外连接,将每行的非聚合字段链接到该行所贡献的聚合结果。我经常好奇他们为什么没有选择。虽然我可能只是无知这个选项:) –

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 
0

这似乎是工作,以及

SELECT * 
FROM makerar m1 
WHERE m1.avg = (SELECT MAX(avg) 
       FROM makerar m2 
       WHERE m1.cname = m2.cname 
       ) 
相关问题