获得多个AVG的MAX由另一列分组

时间:2012-06-29 01:38:57

标签: sql sqlite aggregate-functions

我有一个在保龄球中心使用的查询,可以按降序排列所有最佳平均值。

一切都运作良好,除非如果一名球员在两个不同的联赛中比赛(或者由于每个赛季后平均重置,我不按赛季分组),我只想要给定球员的最佳平均值,因为我不喜欢我想要重复。 (同一个玩家的不同联赛中的平均值不会累积,因此玩家可以拥有多个平均值)

我以为我在询问Stack Overflow(here)之后不久前已经解决了这个问题,但是最近有人告诉我,有时,查询存在一些问题,我不知道如何我之前没有注意到。

问题在于即使我没有得到重复的名字并且我得到了正确的MAX平均值,其他列,例如联盟名称,比赛次数和赛季并不总是正确的,如果玩家玩在多个联赛中。这是查询:

SELECT  PlayerID, Name, max(score)Avg, gamesCount, LeagueName, Season
    FROM( SELECT  PlayerID, Player.Name as Name, Player.Gender as Gender, ROUND(AVG(score),2) as score, COUNT(score) as gamesCount, LeagueName, Season
        FROM  Scores JOIN Players as Player USING(PlayerID)
        WHERE Score > -1 AND bowlout = 'No' AND season = '2011-2012'
        GROUP BY PlayerID, LeagueName, Season
        HAVING gamesCount >= 50
    ) as league_avg
WHERE Gender = 'Male'
GROUP BY PlayerID
ORDER BY Avg DESC LIMIT 0,50;

显然,它不起作用,因为外部查询只按PlayerID进行分组,因此它获得了玩家的最大AVG,但是其他字段,例如,如果玩家有多个联盟参加,则名为leagueName,从他参加的联赛中随机抽出。

我想要的是获得与玩家及其最大平均值相对应的leagueName(以及所有其他信息)。

以下是一个例子:

Name       |      AVG      |   LeagueName
Jones, Tom      122.56          Friday League
Smith, Adam     182.42          Super League
Smith, Adam     194.25          Friendly League
...

预期结果将是:

Name       |      AVG      |   LeagueName
Smith, Adam     194.25          Friendly League
Jones, Tom      122.56          Friday League

我得到了什么:

Name       |      AVG      |   LeagueName
Smith, Adam     194.25          *Super League*
Jones, Tom      122.56          Friday League

正如你所看到的,史密斯,亚当拥有正确的AVG,但错误的联盟与名字/平均组合有关。

我尝试将外部GROUP BY子句更改为PlayerID,LeagueName,Season,但是每个赛季每个联赛重新分配一次,然后我再次获得重复项。除了使用Java应用程序之外,我不知道该尝试什么,抓取所有结果并删除Java中的重复项。显然,我宁愿第一次从SQL查询中获得正确的结果。

作为旁注,即使在本文前面已经提到过,有时查询也没有“AND season ='2011-2012'”部分,所以我不能在不同的季节为同一个玩家获得重复任

编辑:我正在使用SQLite,以防有些人没有注意到这些标签。

3 个答案:

答案 0 :(得分:2)

我相信这样的事情应该有用。

SELECT PlayerID,
       Name,
       Season,
       CAST( SUBSTR(MAX(stats),1,10) AS REAL) AS Average,
       CAST( SUBSTR(MAX(stats),11,10) AS INTEGER) AS GamesCount,
       SUBSTR(MAX(stats),21) AS LeagueName
  FROM (
          SELECT PlayerID,
                 Player.Name as Name,
                 Season,
                 CASE WHEN LENGTH(ROUND(AVG(score),2))-(LENGTH(CAST(AVG(score) AS INTEGER)))=2
                           THEN SUBSTR('          '||(ROUND(AVG(score),2))||'0', -10,10)
                           ELSE SUBSTR('          '||(ROUND(AVG(score),2)), -10,10)
                   END || SUBSTR('          '||COUNT(score),-10,10) || LeagueName as stats
            FROM Scores
            JOIN Players as Player USING(PlayerID)
           WHERE Score > -1
             AND bowlout = 'No'
           GROUP BY PlayerID, Player.Name, LeagueName, Season
           HAVING COUNT(score) >= 50
       ) AS league_avg
 WHERE Season = '2011-2012'
 GROUP BY PlayerID, Name, Season
 ORDER BY Average DESC LIMIT 0,50
;

我从未使用过SQLite,所以如果我有一些错误的语法,请不要感到惊讶。它尝试将Average,GameCount和LeagueName连接成一个可以按平均值轻松排序的字符串,并且还可以使用子字符串操作轻松提取组件。

我最不确定的部分是平均值的格式。我使用了at the bottom of this link找到的语法。

答案 1 :(得分:1)

正如 Andriy M 在评论中发布的那样,有一种解决方法可以让聚合函数为不在GROUP BY子句中的列获得正确的结果。

使用该解决方法与未来版本的兼容性问题是不安全的,因为它没有在SQLite规范中定义,但它在这种特殊情况下适用于我而不会减慢查询速度,这正是我想要的。

我不打算将来升级我的SQLite版本,因为我已经计划将我的应用程序放在一个MySQL数据库上,所以我觉得发布这个答案是合理的,因为它完美地解决了我的问题。 / p>

诀窍是在字段的内部查询中使用ORDER BY作为平均值。它的工作原理是,当外部查询尝试GROUP BY PlayerID时,未按使用者分组的其他列是与分组的字段的最后一个实例一起使用的列。因此,如果PlayerID有三个不同的平均值,则在内部查询中,最高平均值将是最后一个,因此外部查询将使用该特定PlayerID的最后一个实例附带的字段。

这是代码,添加的行有一个注释:

SELECT  PlayerID, Name, max(score)Avg, gamesCount, LeagueName, Season
FROM( SELECT  PlayerID, Player.Name as Name, Player.Gender as Gender, ROUND(AVG(score),2) as score, COUNT(score) as gamesCount, LeagueName, Season
    FROM  Scores JOIN Players as Player USING(PlayerID)
    WHERE Score > -1 AND bowlout = 'No' AND season = '2011-2012'
    GROUP BY PlayerID, LeagueName, Season
    HAVING gamesCount >= 50
    ORDER BY score /* Here is the added line that solves it all */
) as league_avg
WHERE Gender = 'Male'
GROUP BY PlayerID
ORDER BY Avg DESC LIMIT 0,50;

答案 2 :(得分:0)

您希望通过修饰符

了解WITH ROLLUP