前言:
在某些應用場景中,我們經常會遇到一些排名的問題,比如按成績或年齡排名。排名也有多種排名方式,如直接排名、分組排名,排名有間隔或排名無間隔等等,這篇文章將總結幾種mysql中常見的排名問題。
建立測試表
create table scores_tb (
id int auto_increment primary key,
xuehao int not null,
score int not null
) engine=innodb default charset=utf8;
insert into scores_tb (xuehao,score) values (1001,89),(1002,99),(1003,96),(1004,96),(1005,92),(1006,90),(1007,90),(1008,94);
# 檢視下插入的資料
mysql> select * from scores_tb;
+----+--------+-------+
| id | xuehao | score |
+----+--------+-------+
| 1 | 1001 | 89 |
| 2 | 1002 | 99 |
| 3 | 1003 | 96 |
| 4 | 1004 | 96 |
| 5 | 1005 | 92 |
| 6 | 1006 | 90 |
| 7 | 1007 | 90 |
| 8 | 1008 | 94 |
+----+--------+-------+
1.普通排名
按分數高低直接排名,從1開始,往下排,類似於row number。下面我們給出查詢語句及排名結果。
# 查詢語句
select xuehao, score, @currank := @currank + 1 as rank
from scores_tb, (
select @currank := 0
) rorder by score desc;
# 排序結果
+--------+-------+------+
| xuehao | score | rank |
+--------+-------+------+
| 1002 | 99 | 1 |
| 1003 | 96 | 2 |
| 1004 | 96 | 3 |
| 1008 | 94 | 4 |
| 1005 | 92 | 5 |
| 1006 | 90 | 6 |
| 1007 | 90 | 7 |
| 1001 | 89 | 8 |
+--------+-------+------+
上述查詢語句中,我們申明了乙個變數 @currank ,並將此變數初始化為0,查得一行將此變數加一,並以此作為排名。我們看到這類排名是沒間隔的並且有些分數相同但排名不同。
2.分數相同,名次相同,排名無間隔
# 查詢語句
select xuehao, score,
case
when @prevrank = score then @currank
when @prevrank := score then @currank := @currank + 1
end as rank
from scores_tb,
(select @currank :=0, @prevrank := null) r
order by score desc;
# 排名結果
+--------+-------+------+
| xuehao | score | rank |
+--------+-------+------+
| 1002 | 99 | 1 |
| 1003 | 96 | 2 |
| 1004 | 96 | 2 |
| 1008 | 94 | 3 |
| 1005 | 92 | 4 |
| 1006 | 90 | 5 |
| 1007 | 90 | 5 |
| 1001 | 89 | 6 |
+--------+-------+------+
3.並列排名,排名有間隔
另外一種排名方式是相同的值排名相同,相同值的下乙個名次應該是跳躍整數值,即排名有間隔。
# 查詢語句
select xuehao, score, rank from
(select xuehao, score,
@currank := if(@prevrank = score, @currank, @incrank) as rank,
@incrank := @incrank + 1,
@prevrank := score
from scores_tb, (
select @currank :=0, @prevrank := null, @incrank := 1
) r
order by score desc) s;
# 排名結果
+--------+-------+------+
| xuehao | score | rank |
+--------+-------+------+
| 1002 | 99 | 1 |
| 1003 | 96 | 2 |
| 1004 | 96 | 2 |
| 1008 | 94 | 4 |
| 1005 | 92 | 5 |
| 1006 | 90 | 6 |
| 1007 | 90 | 6 |
| 1001 | 89 | 8 |
+--------+-------+------+
上面介紹了三種排名方式,實現起來還是比較複雜的。好在mysql8.0增加了視窗函式,使用內建函式可以輕鬆實現上述排名。
mysql8.0 利用視窗函式實現排名
mysql8.0中可以利用 row_number(),dense_rank(),rank() 三個視窗函式實現上述三種排名,需要注意的一點是as後的別名,千萬不要與前面的函式名重名,否則會報錯,下面給出這三種函式實現排名的案例:
# 三條語句對於上面三種排名
select xuehao,score, row_number() over(order by score desc) as row_r from scores_tb;
select xuehao,score, dense_rank() over(order by score desc) as dense_r from scores_tb;
select xuehao,score, rank() over(order by score desc) as r from scores_tb;
# 一條語句也可以查詢出不同排名
select xuehao,score,
row_number() over w as 'row_r',
dense_rank() over w as 'dense_r',
rank() over w as 'r'
from `scores_tb`
window w as (order by `score` desc);
# 排名結果
+--------+-------+-------+---------+---+
| xuehao | score | row_r | dense_r | r |
+--------+-------+-------+---------+---+
| 1002 | 99 | 1 | 1 | 1 |
| 1003 | 96 | 2 | 2 | 2 |
| 1004 | 96 | 3 | 2 | 2 |
| 1008 | 94 | 4 | 3 | 4 |
| 1005 | 92 | 5 | 4 | 5 |
| 1006 | 90 | 6 | 5 | 6 |
| 1007 | 90 | 7 | 5 | 6 |
| 1001 | 89 | 8 | 6 | 8 |
+--------+-------+-------+---------+---+
總結:本文給出三種不同場景下實現統計排名的sql,可以根據不同業務需求選取合適的排名方案。對比mysql8.0,發現利用視窗函式可以更輕鬆實現排名,其實業務需求遠遠比我們舉的示例要複雜許多,用sql實現此類業務需求還是需要慢慢積累的。
參考:
sql如何實現百分比統計 教你用SQL實現統計排名
前言 在某些應用場景中,我們經常會遇到一些排名的問題,比如按成績或年齡排名。排名也有多種排名方式,如直接排名 分組排名,排名有間隔或排名無間隔等等,這篇文章將總結幾種mysql中常見的排名問題。建立測試表 create table scores tb id int auto increment pr...
SQL實現統計排名
在某些應用場景中,我們經常會遇到一些排名的問題,比如按成績或年齡排名。排名也有多種排名方式,如直接排名 分組排名,排名有間隔或排名無間隔等等,這篇文章將總結幾種mysql中常見的排名問題。create table scores tb id int auto increment primary key...
教你用Ruby算命!
本文又名 看看我的破機器能算多少個梅森數出來 如下,mersennes def is prime?n 這裡是用了費馬小定理,很慢很慢!2.n 1 each end 1.13 each do n m 2 n 1 mersennes 上面這個寫法,2 x要計算兩次,寫法好看,但效能很低 p mersen...