MySQL跨多列排名
Posted
技术标签:
【中文标题】MySQL跨多列排名【英文标题】:MySQL rank across multiple columns 【发布时间】:2017-01-20 14:52:13 【问题描述】:我们有一个包含以下数据的 mysql 表(结果)
|nid|uid|score|duration
在这张表中,我们有多个测验的结果, 我们需要提出一个查询,该查询将为我们提供每个测验的用户排名输出。
排名必须基于最高分到最低分,如果分数与最低持续时间和最高持续时间相关。我们目前有这个查询:
SELECT nid, uid, score, duration, rank FROM
(SELECT nid, uid, score, duration,
@curRank := IF(@prevRank = score + duration, @curRank, @incRank) AS rank,
@incRank := @incRank + 1,
@prevRank := score
FROM results p, (
SELECT @curRank :=0, @prevRank := NULL, @incRank := 1
) r WHERE nid = 1
ORDER BY score DESC, duration ASC) s
但是在得分和持续时间相同的情况下,他们将获得不同的排名
输出
|nid|uid|score|duration|rank
|1 |1 |90 |100 |1
|1 |2 |90 |150 |2
|1 |3 |90 |150 |3
|1 |4 |80 |100 |4
|1 |5 |80 |200 |5
|1 |6 |70 |300 |6
期望的输出
|nid|uid|score|duration|rank
|1 |1 |90 |100 |1
|1 |2 |90 |150 |2
|1 |3 |90 |150 |2
|1 |4 |80 |100 |4
|1 |5 |80 |200 |5
|1 |6 |70 |300 |6
【问题讨论】:
已解决:缺少@prevRank := score + duration 如果下一行的分数和时长不同但总和与上一行相同怎么办? 尝试用@incRank := @curRank + 1,
替换@incRank := @incRank + 1,
Jan 你的建议仍然输出相同的结果
@GurV 这是一个好点,你有什么建议来解决这个问题吗?
【参考方案1】:
您需要为列使用单独的变量。另外,在子查询中进行排序,然后应用交叉连接并查找排名等。
试试这个:
SELECT
t.*,
@rank:=IF((@rn:=@rn + 1) IS NOT NULL,
IF(score = @lastscore
AND duration = @lastduration,
@rank,
IF((@lastscore:=score) IS NOT NULL
AND (@lastduration:=duration) IS NOT NULL,
@rn,
1)),
1) rank
FROM
(SELECT
*
FROM
results
WHERE nid = 1
ORDER BY score DESC , duration , uid) t
CROSS JOIN
(SELECT @rank:=0, @lastscore:=- 1, @lastduration:=- 1, @rn:=0) t2
您可能会想在不同的列中分别分配值(就像您在问题中所做的那样)。 请注意,这种方法可能不会像您预期的那样工作,因为 MySQL 可能不会按照所列列的顺序进行分配。
MySQL documentation 对此非常清楚:
作为一般规则,您永远不应该为用户变量赋值 并读取同一语句中的值。你可能会得到 您期望的结果,但这不能保证。的顺序 涉及用户变量的表达式的评估是未定义的,并且 可能会根据给定语句中包含的元素而更改; 另外,这个顺序不保证在 MySQL 服务器的版本。在 SELECT @a, @a:=@a+1, ... 中,您可能 认为 MySQL 会先评估 @a 然后做一个赋值 第二。但是,更改语句(例如,通过添加 GROUP BY、HAVING 或 ORDER BY 子句)可能会导致 MySQL 选择一个 具有不同评估顺序的执行计划。
演示@SQLFiddle
【讨论】:
我真的很喜欢这个并且效果很好,我添加了一个 WHERE nid = 1,因为在同一个表格中有多个测验结果是必需的 @GHaddon 已更新。看起来怎么样? 我正在寻找同样的东西,但我希望排名数线性上升。这看起来如何。提前谢谢。以上是关于MySQL跨多列排名的主要内容,如果未能解决你的问题,请参考以下文章