SQL应用题(二)之分数排名

题目

编写一个 SQL 查询来实现分数排名。

如果两个分数相同,则两个分数排名(Rank)相同。请注意,平分后的下一个名次应该是下一个连续的整数值。换句话说,名次之间不应该有“间隔”。

+----+-------+
| Id | Score |
+----+-------+
| 1  | 3.50  |
| 2  | 3.65  |
| 3  | 4.00  |
| 4  | 3.85  |
| 5  | 4.00  |
| 6  | 3.65  |
+----+-------+

例如,根据上述给定的 Scores 表,你的查询应该返回(按分数从高到低排列):

| Score | Rank |
+-------+------+
| 4.00  | 1    |
| 4.00  | 1    |
| 3.85  | 2    |
| 3.65  | 3    |
| 3.65  | 3    |
| 3.50  | 4    |
+-------+------+

重要提示:对于 MySQL 解决方案,如果要转义用作列名的保留字,可以在关键字之前和之后使用撇号。例如 Rank

解题思路

方法一

首先看到这道题,主要需要我们做的时挨个比较每个分数,然后排序,如果有相同的分数,排序不增加,继续下面的排序,想到了 @,:= 符号的作用,设置变量并赋初始值。具体思路如下:

1.有点类似Java的自增,设置一个变量并赋初值,循环一次自增加1,从而实现排序;

2.MySQL是先将数据查询出来并按照需要排序字段按升序 ASC 或者降序 DESC 排序,设置好排序的初始值为0;

3.将已经排序好的数据从第一条依次取出,取一条就自增加1,实现1到最后的排名;

4.当出现相同的排名时,排名不变,则需要再设置一个变量,用来记录上一条数据的值,跟当前的数据进行比较,如果相同则排名不变,不相同则排名加1。

解题逻辑相对复杂,直接上代码吧。

SELECT
s.Score AS Score,
CASE
WHEN @rowtotal = s.Score THEN
    @rownum
WHEN @rowtotal := s.Score THEN
    @rownum :=@rownum + 1
WHEN @rowtotal = 0 THEN
    @rownum :=@rownum + 1
END AS Rank
FROM 
(
    SELECT 
    Score
    FROM scores
    ORDER BY Score DESC
) AS s,
(SELECT @rownum := 0,@rowtotal := NULL) r

方法二

上面方法比较复杂,理解起来也是挺让人头秃的,有一个更好理解的,来,直接上代码:

SELECT
s.Score Score,
(
    SELECT COUNT(DISTINCT sc.Score)
    FROM scores sc
    WHERE sc.Score >= s.Score
) AS `Rank`
FROM scores s
ORDER BY `Rank` ASC
分析SQL

先看里面的一层SQL,

SELECT COUNT(DISTINCT sc.Score)
FROM scores sc
WHERE sc.Score >= s.Score

先是查询所有分数,按照结果顺序依次跟每一个分数比较,计算大于等于该分数的分数,每次比较的结果分为一组,然后对每组的结果分数去重求总数,就是它的排名

| Score |
+-------+
| 4.00  |
| 4.00  |
| 3.85  |
| 3.65  |
| 3.65  |
| 3.50  |
+-------+

最后对排名进行升序排序得出最后结果,这种方法类似Java的双层嵌套循环,遍历数据比较大小求和,理解起来还是很容易的

方法三

上面都是用MySQL解题的,其实使用Sql Server去解决是最简单的,一句SQL就完事儿,上代码:

select Score,dense_rank() over(order by Score DESC
) as Rank from Scores

主要用到的是dense_rank()函数,用于解决这道题再合适不过了,Sql Server还有其他集中排名函数,分别是 ROW_NUMBER()RANK()NTILE()

1.ROW_NUMBER() 的用法是先通过 OVER 实现排序,然后按照这个顺序生成排序号,可以用于分页,例如:

select ROW_NUMBER() OVER(order by SubTime desc) as row_num,* from Order

2.RANK() 函数与row_number函数不同的是,rank函数考虑到了over子句中排序字段值相同的情况,如果使用rank函数来生成序号,over子句中排序字段值相同的序号是一样的,后面字段值不相同的序号将跳过相同的排名号排下一个,也就是相关行之前的排名数加一,代码如下:

select RANK() OVER(order by UserId) as rank,* from Order 

查询结果如下图:

| rank | userId |
+-------+------+
|   1   | 1    |
|   1   | 1    |
|   3   | 2    |
|   4   | 3    |
|   4   | 3    |
|   6   | 4    |
+-------+------+

拓展

既然已经提到第一种方法了,这里就拓展一下,SQL的几种排名方法,主要讲MySQL数据语言的。

(1)依次递增排名

要用到 @,:= ,给 rownum 赋值,逻辑前面已经提到过,可以参考方法一的第3点,直接上代码:

SELECT 
s.Score AS Score,
@rownum := @rownum + 1 AS Rank
FROM 
(
    SELECT 
    Score
    FROM scores
    ORDER BY Score DESC
) AS s,
(SELECT @rownum := 0) r

查询结果如下,

| Score | Rank |
+-------+------+
| 4.00  |  1   |
| 4.00  |  2   |
| 3.85  |  3   |
| 3.65  |  4   |
| 3.65  |  5   |
| 3.50  |  6   |
+-------+------+

(2)数据相同,排名相同,排名无间隔

这种排名方法,就是方法一的排名方法,可以直接参考,这里不再赘述

(3)数据相同,排名相同,排名有间隔

先上查询结果,展示下效果,

| Score | Rank |
+-------+------+
| 4.00  |  1   |
| 4.00  |  1   |
| 3.85  |  3   |
| 3.65  |  4   |
| 3.65  |  4   |
| 3.50  |  6   |
+-------+------+

这种方法是在在第二种方法的基础,还要多一个变量记录排序的号码 incrnum,直接看代码,

SELECT
    obj_new.Score,
    obj_new.Rank
FROM
(
    SELECT
        obj.Score,
        @rownum := @rownum + 1 AS num_tmp,
        @incrnum := CASE
        WHEN @rowtotal = obj.score THEN
        @incrnum
        WHEN @rowtotal := obj.score THEN
        @rownum
        END AS Rank
    FROM
    (
        SELECT score
        FROM scores
        ORDER BY score DESC
    ) AS obj,
    (
        SELECT @rownum := 0 ,@rowtotal := NULL ,@incrnum := 0
    ) r
) AS obj_new

END

以上就是今天的SQL应用题啦,关于SQL排名的测试题,有不对的地方,望指正

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值