教你用SQL實現統計排名

前言: html

在某些應用場景中,咱們常常會遇到一些排名的問題,好比按成績或年齡排名。排名也有多種排名方式,如直接排名、分組排名,排名有間隔或排名無間隔等等,這篇文章將總結幾種MySQL中常見的排名問題。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。下面咱們給出查詢語句及排名結果。sql

# 查詢語句
SELECT xuehao, score, @curRank := @curRank + 1 AS rank
FROM scores_tb, (
SELECT @curRank := 0
) r
ORDER 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增長了窗口函數,使用內置函數能夠輕鬆實現上述排名。spa

MySQL8.0 利用窗口函數實現排名

MySQL8.0中能夠利用 ROW_NUMBER(),DENSE_RANK(),RANK() 三個窗口函數實現上述三種排名,須要注意的一點是as後的別名,千萬不要與前面的函數名重名,不然會報錯,下面給出這三種函數實現排名的案例:.net

# 三條語句對於上面三種排名
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 |
+--------+-------+-------+---------+---+

總結: code

本文給出三種不一樣場景下實現統計排名的SQL,能夠根據不一樣業務需求選取合適的排名方案。對比MySQL8.0,發現利用窗口函數能夠更輕鬆實現排名,其實業務需求遠遠比咱們舉的示例要複雜許多,用SQL實現此類業務需求仍是須要慢慢積累的。htm

參考: blog

WeChat

本文由博客一文多發平臺 OpenWrite 發佈!
相關文章
相關標籤/搜索