必须出现在GROUP BY子句中或用于聚合函数中
我有一个表,看起来像这个调用者'makerar'
cname | wmname | avg --------+-------------+------------------------ canada | zoro | 2.0000000000000000 spain | luffy | 1.00000000000000000000 spain | usopp | 5.0000000000000000
我想select每个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
我怎样才能解决这个问题?
注意:该表是从以前的操作创build的VIEW。
是的,这是一个常见的聚合问题。 在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 ;
这个方法唯一的一件事就是它会显示所有的logging(窗口函数不分组)。 但是它会显示每行中国家的正确(即在cname
级别MAX
) MAX
,所以取决于您:
cname | wmname | mx --------+--------+------------------------ canada | zoro | 2.0000000000000000 spain | luffy | 5.0000000000000000 spain | usopp | 5.0000000000000000
该解决scheme,可以说是不太优雅,以显示唯一的(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
有趣的是,尽pipespec规范允许select非分组字段,但是主要的引擎似乎不太喜欢它。 Oracle和SQLServer根本就不允许这样做。 默认情况下,Mysql允许使用它,但现在从5.7开始,pipe理员需要在服务器configuration中手动启用此选项( ONLY_FULL_GROUP_BY
),以支持此function。
在Postgres中,你也可以使用特殊的DISTINCT ON (expression)
语法:
SELECT DISTINCT ON (cname) cname, wmname, avg FROM makerar ORDER BY cname, avg DESC ;
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()
窗口函数 :
SELECT cname, wmname, avg FROM ( SELECT cname, wmname, avg, rank() OVER (PARTITION BY cname ORDER BY avg DESC) FROM makerar) t WHERE rank = 1;
注意
每一个都将保留每个组的多个最大值。 如果你想要每个组只有一个logging,即使有多个logging的平均等于最大值,你应该检查@ ypercube的答案。
group by
select指定组中的非分组和非聚合字段的问题在于引擎无法知道在这种情况下应该返回哪个logging的字段。 它是第一个吗? 它是最后? 通常没有自然对应于汇总结果的logging( min
和max
是例外)。
但是,有一个解决方法:使所需的字段聚合以及。 在posgres,这应该工作:
SELECT cname, (array_agg(wmname ORDER BY avg DESC))[1], MAX(avg) FROM makerar GROUP BY cname;
请注意,这将创build一个由avgsorting的所有wname数组,并返回第一个元素(postgres中的数组是基于1的)。
我最近碰到这个问题,当试图用case when
来计数的case when
,发现改变where和count
语句的顺序就修复了这个问题:
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
而不是在后者,我得到的错误,苹果和橘子应该出现在总function
CASE WHEN ((apples = 'TRUE' OR oranges 'TRUE') THEN COUNT(*) END) END AS fruit_counter
这似乎也工作
SELECT * FROM makerar m1 WHERE m1.avg = (SELECT MAX(avg) FROM makerar m2 WHERE m1.cname = m2.cname )