MySQL - 检索按可变系列列的最大值排序的行

时间:2014-10-14 00:06:13

标签: mysql aggregate

我在使用特定的MySQL查询时非常困难。 这是我需要连接的3个表的结构,并从以下行开始排序:

users
ID    username

routes
ID    val_1       val_2      val_3       val_4

scores
ID    route_id    user_id    route_level

此查询用于排行榜功能,需要返回按其最佳分数排序的用户ID列表。一个用户可能有几个分数,我们需要找到最佳分数。

扭曲是“route_level”部分:如果此列的值是例如“2”,那么我们需要转到相应的route_id,并找到val_1和val_2之间的最大值。我们必须不高于val_x,x是列得分的值.route_level。

此外,val_x不是int(值通常类似于“6A +”,下一个更大的值是6B,然后是6B +等),而val_x不一定小于val_x + 1.

这是我得到的,但它不起作用(我得到的结果遍布整个地方,根本没有订购,至少不是我能理解的方式):

SELECT u.*, r.val_1 AS v1, r.val_2 AS v2, r.val_3 AS v3, r.val_4 AS v4 
FROM users u
INNER JOIN scores s
ON s.user_id = u.ID
INNER JOIN routes r
ON s.route_id = r.ID
GROUP BY u.ID
ORDER BY GREATEST(v1, v2, v3, v4) DESC

你们有没有想过如何在一个MySQL查询中使用它?

谢谢! : - )

编辑:这是SQLFiddle link

3 个答案:

答案 0 :(得分:0)

您需要获得正确的汇总查询才能使您的订购正确。试试这个。它将正确处理用户的聚合,并为每个用户提取最大val_n分数。

SELECT u.ID, u.username, 
       MAX(r.val_1) AS v1,
       MAX(r.val_2) AS v2, 
       MAX(r.val_3) AS v3, 
       MAX(r.val_4) AS v4 
  FROM users u
 INNER JOIN scores s  ON s.user_id = u.ID
 INNER JOIN routes r ON s.route_id = r.ID
 GROUP BY u.ID ,u.username
 ORDER BY GREATEST( MAX(r.val_1), MAX(r.val_2), MAX(r.val_3), MAX(r.val_4)) DESC

编辑我错过了关于route_level的一些内容...过分关注问题中的查询,而不是文本。

但它并不是很难应用。它需要在详细级别逐行应用,在聚合函数MAX()之前。我相信,可以通过更改查询的SELECT部分来完成此操作。

SELECT u.ID, u.username, 
       MAX(GREATEST (
           IF(r.route_level>=1, r.val_1, NULL)
           IF(r.route_level>=2, r.val_2, NULL)
           IF(r.route_level>=3, r.val_3, NULL)
           IF(r.route_level>=4, r.val_4, NULL) )) AS val
  FROM users u
 INNER JOIN scores s  ON s.user_id = u.ID
 INNER JOIN routes r ON s.route_id = r.ID
 GROUP BY u.ID ,u.username
 ORDER BY MAX(GREATEST (
           IF(r.route_level>=1, r.val_1, NULL)
           IF(r.route_level>=2, r.val_2, NULL)
           IF(r.route_level>=3, r.val_3, NULL)
           IF(r.route_level>=4, r.val_4, NULL) ))

我也错过了关于val_n值为varchar()值而不是整数的观点。那个更难。在MAX()值上使用varchar()时,它会使用排序规则。我知道的所有字符排序规则都会声明值6B+例如大于10B+,因为6大于1

对不起,我不知道任何优雅的SQL魔法可以让它更好地工作。

答案 1 :(得分:0)

你看起来真的很近......

但是你需要一个聚合函数来获得“最佳分数”。 (除了MySQL以外的数据库会反对你的陈述,抓住对未包含在GROUP BY中的非聚合的引用...)

关于route_level的“扭曲”有点棘手。我先处理,从每一行得到“最好”的分数。我会使用这样的表达式:

IF(r.route_level>=1,r.val_1,NULL)
IF(r.route_level>=2,r.val_2,NULL)
IF(r.route_level>=3,r.val_3,NULL)
IF(r.route_level>=4,r.val_4,NULL)

这些表达式有条件地从特定val_N列返回一个值,具体取决于route_level的值。由于您不需要返回单个值,因此可以将所有这些表达式包装在方便的GREATEST()函数中。

GREATEST( IF(r.route_level>=1,r.val_1,NULL)
        , IF(r.route_level>=2,r.val_2,NULL)
        , IF(r.route_level>=3,r.val_3,NULL)
        , IF(r.route_level>=4,r.val_4,NULL)
        , ...
        )

这将从每一行获得“最佳分数”。现在剩下的就是从给定用户返回的多行中找到“最佳分数”。我们可以使用MAX聚合来做到这一点。

因此,您需要对查询进行的唯一更改是替换ORDER BY子句中的表达式。像这样:

SELECT u.ID
  FROM users u
  JOIN scores s
    ON s.user_id = u.ID
  JOIN routes r
    ON s.route_id = r.ID
 GROUP BY u.ID
 ORDER BY MAX( GREATEST( IF(r.route_level>=1,r.val_1,NULL)
                       , IF(r.route_level>=2,r.val_2,NULL)
                       , IF(r.route_level>=3,r.val_3,NULL)
                       , IF(r.route_level>=4,r.val_4,NULL)
                       )
             ) DESC

(同样从SELECT列表中删除所有那些val_1,val_2等表达式。不确定哪些行将返回。这些值中没有一个可能是“最佳分数”。如果你还想返回“最佳得分”的值,然后在SELECT列表的ORDER BY中使用相同的表达式。

答案 2 :(得分:0)

结合Ollie Jones和spencer7593的答案,我找到了自己问题的答案:

SELECT u.ID, u.username, s.route_level,
   GREATEST( IF(s.route_level >= 1, MAX(r.val_1), 0)
            , IF(s.route_level >= 2, MAX(r.val_2), 0)
            , IF(s.route_level >= 3, MAX(r.val_3), 0)
            , IF(s.route_level >= 4, MAX(r.val_4), 0)
            ) AS vmax
 FROM users u
 INNER JOIN scores s  ON s.user_id = u.ID
 INNER JOIN routes r ON s.route_id = r.ID
 GROUP BY u.ID
 ORDER BY vmax DESC

这给出了最大值AS vmax,考虑了每行的route_length值,并正确排序结果。

感谢大家的参与! : - )