教你用SQL實現統計排名

2022-05-08 07:27:07 字數 4848 閱讀 2817

前言:

在某些應用場景中,我們經常會遇到一些排名的問題,比如按成績或年齡排名。排名也有多種排名方式,如直接排名、分組排名,排名有間隔或排名無間隔等等,這篇文章將總結幾種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...