在MySQL中排名结果时如何处理并列情况?

7
当在MySQL查询中排列结果时,如何处理并列情况?在本示例中,我已经简化了表名和列名,但它应该能够说明我的问题:
SET @rank=0;

   SELECT student_names.students, 
          @rank := @rank +1 AS rank, 
          scores.grades
     FROM student_names  
LEFT JOIN scores ON student_names.students = scores.students
 ORDER BY scores.grades DESC

因此,请想象上述查询产生的结果:
Students  Rank  Grades
=======================
Al         1     90
Amy        2     90
George     3     78
Bob        4     73
Mary       5     NULL
William    6     NULL

尽管Al和Amy的成绩相同,但其中一个排名高于另一个。Amy被欺骗了。我该如何使Amy和Al拥有相同的排名,以便他们都有排名1。此外,William和Mary没有参加测试。他们逃课并在男生洗手间吸烟。他们应该并列最后一名。

正确的排名应为:

Students  Rank  Grades
========================
Al         1     90
Amy        1     90
George     2     78
Bob        3     73
Mary       4     NULL
William    4     NULL

如果有任何建议,请告诉我。

2个回答

16

编辑:此方法支持MySQL 4.1+

使用:

   SELECT st.name,
          sc.grades,
          CASE 
            WHEN @grade = COALESCE(sc.grades, 0) THEN @rownum 
            ELSE @rownum := @rownum + 1 
          END AS rank,
          @grade := COALESCE(sc.grades, 0)
     FROM STUDENTS st
LEFT JOIN SCORES sc ON sc.student_id = st.id
     JOIN (SELECT @rownum := 0, @grade := NULL) r
 ORDER BY sc.grades DESC

在MySQL中,您可以使用交叉连接(即没有任何条件的INNER JOIN)来声明和使用变量,而无需使用单独的SET语句。

您需要使用COALESCE来正确处理NULL值。


嗨OMG Ponies,你是MySQL终结者!!太棒了,非常感谢你的帮助。我永远不会想到这个解决方法。(我的MySQL知识很基础,所以我需要学习你的答案才能更好地理解它)。另外,我喜欢你的屏幕名字。再次感谢!!!-Laxmidi - Laxmidi
你可以使用交叉连接(在MySQL中,没有任何条件的内部连接)来声明和使用变量,而无需使用单独的SET语句。天啊,我不知道你可以这样做。+1 - heisenberg
OMG Ponies - 我试用了你的解决方案,它完美地运行了。但我必须承认,我仍然不太确定它是如何工作的。你能否添加一些关于如何让成绩相同的学生获得相同分数的详细信息?或者是否有其他地方的链接描述了这种技术。谢谢! - Brian Armstrong
@Brian Armstrong:当等级变量等于当前行或零时,CASE语句将重复rownum值。否则,排名值将增加并显示给该行。SELECT子句中的最后一列将等级值设置为与下一个等级值进行比较的变量。ORDER BY确保具有最高排名的等级具有最低数字。 - OMG Ponies

1

听起来像是一个中间件规则,最好在数据库和客户端之间的代码中表达。

如果不可能的话,我建议在MySQL中使用存储过程运行您编写的查询,然后使用游标和数组修改结果。


嗨,duffymo,谢谢您的评论。我很感谢您的帮助。-Laxmidi - Laxmidi
OMG Ponies 在这个问题上真是太棒了。回答太棒了。 - duffymo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接