编写一个 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,
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 Order2.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数据语言的。
要用到 @,:= ,给 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 | +-------+------+这种排名方法,就是方法一的排名方法,可以直接参考,这里不再赘述
先上查询结果,展示下效果,
| 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以上就是今天的SQL应用题啦,关于SQL排名的测试题,有不对的地方,望指正