MySQL在多个列中排名

时间:2017-01-20 14:52:14

标签: mysql

我们有一个包含以下数据的MySQL表(结果)

|nid|uid|score|duration

在此表中,我们得到了多个quizes的结果, 我们需要提出一个查询,它将为我们提供每个测验的用户排名输出。

排名必须基于最高得分到最低得分,并且如果得分是最低持续时间与最高持续时间相关联。我们目前有这个查询:

  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

1 个答案:

答案 0 :(得分:2)

您需要为列使用单独的变量。此外,在子查询中进行排序,然后然后应用交叉连接并查找排名等。

试试这个:

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