Write a SQL query to rank scores. If there is a tie between two scores, both should have the same ranking. Note that after a tie, the next ranking number should be the next consecutive integer value. In other words, there should be no "holes" between ranks.
+----+-------+ | Id | Score | +----+-------+ | 1 | 3.50 | | 2 | 3.65 | | 3 | 4.00 | | 4 | 3.85 | | 5 | 4.00 | | 6 | 3.65 | +----+-------+
For example, given the above Scores
table, your query should generate the following report (order by highest score):
+-------+------+ | Score | Rank | +-------+------+ | 4.00 | 1 | | 4.00 | 1 | | 3.85 | 2 | | 3.65 | 3 | | 3.65 | 3 | | 3.50 | 4 | +-------+------+
解法:
1. With Variables(用户定义变量): 700 ms
First one uses two variables, one for the current rank and one for the previous score.
SELECT Score, @rank := @rank + (@prev <> (@prev := Score)) Rank FROM Scores, (SELECT @rank := 0, @prev := -1) init ORDER BY Score desc
这种方法主要的思想就是基于变量。关键点是 选择排名和上一个分数 两个变量, 变量的初始化,判断相同的分数的排名。mysql不像SQL有4个排名的函数可以调用,因此要自己来写排名的功能。
类似的实现:
SELECT Score, Rank FROM( SELECT Score, @curRank := @curRank + IF(@prevScore = Score, 0, 1) AS Rank, @prevScore := Score FROM Scores s, (SELECT @curRank := 0) r, (SELECT @prevScore := NULL) p ORDER BY Score DESC ) t;
2. Always Count: 1322 ms
This one counts, for each score, the number of distinct greater or equal scores.
SELECT Score, (SELECT count(distinct Score) FROM Scores WHERE Score >= s.Score) Rank FROM Scores s ORDER BY Score desc
没太看懂这种算法的思想。
补充知识:Mysql 用户自定义变量详解
你可以利用SQL语句将值存储在用户自定义变量中,然后再利用另一条SQL语句来查询用户自定义变量。这样以来,可以再不同的SQL间传递值。
用户自定义变量的声明方法形如:@var_name,其中变量名称由字母、数字、“.”、“_”和“$”组成。当然,在以字符串或者标识符引用时也可以包含其他字符(例如:@’my-var’,@”my-var”,或者@`my-var`)。
用户自定义变量是会话级别的变量。其变量的作用域仅限于声明其的客户端链接。当这个客户端断开时,其所有的会话变量将会被释放。用户自定义变量是不区分大小写的。
使用SET语句来声明用户自定义变量:
1 SET @var_name = expr[, @var_name = expr] ...
在使用SET设置变量时,可以使用“=”或者“:=”操作符进行赋值。当然,除了SET语句还有其他赋值的方式。比如下面这个例子,但是赋值操作符只能使用“:=”。因为“=”操作符将会被认为是比较操作符。
mysql> SET @t1=1, @t2=2, @t3:=4; mysql> SELECT @t1, @t2, @t3, @t4 := @t1+@t2+@t3;