前言:
在某些應用場景中,我們經常會遇到一些排名的問題,比如按成績或年齡排名。排名也有多種排名方式,如直接排名、分組排名,排名有間隔或排名無間隔等等,這篇文章將總結幾種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 rankfrom 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 + 1end as rankfrom scores_tb, (select @currank :=0, @prevrank := null) rorder 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 := scorefrom 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 並沒有乙個很直接的方式達到這個需求。要以 sql 算出累積總計,基本上的概念與列出排名類似 第一是先做個 自我鏈結 self join 然後將結果依序列出。在做列出排名時,我們算出每一行之前 包含那一行本身 有多少行數 而在做累積總計時,我們則是算出每一...
sql操作歸類百分比
原資料 專案名稱 完成否 主閘 是 主閘 是 主閘 否 發動機 是 發動機 否 發動機 否 鏈條 是 鏈條 是 鏈條 否 想要得到的資料 專案名稱 完成百分比 主閘 2 3 發動機 1 3鏈條 2 3sql語句應該如何寫?select name,convert varchar 100 sum cas...
sql語句求百分比
此sql語句包括了兩個聚合函式做除法求百分比,並保留兩位小數,直接輸出字串形式的百分比。以及對case when在聚合函式的應用。select ss.ss name,ss id,count ea.ea id as eacounts,回單交換單個數 count eb.eb id as ebcounts...