序文:
一部のアプリケーションシナリオでは、グレードや年齢によるランキングなど、ランキングの問題が発生することがよくあります。ランキングには、直接ランキング、グループランキング、スペースありまたはスペースなしのランキングなど、多くのランキング方法があります。この記事では、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.普通のランキング
行番号と同様に、スコアに基づいて直接ランク付けします。以下に、クエリステートメントとランキング結果を示します。
# 查询语句
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に初期化し、この変数に1を追加してランキングとして使用する1行を見つけました。そのようなランキングは中断されておらず、一部のスコアは同じであるが、ランキングが異なることがわかります。
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 |
+--------+-------+------+
上記の3つのランク付け方法は、実装が比較的複雑です。幸い、MySQL8.0にはウィンドウ関数が追加されており、組み込み関数を使用して上記のランキングを簡単に達成できます。
MySQL8.0はウィンドウ関数を使用してランキングを達成します
MySQL 8.0では、3つのウィンドウ関数ROW_NUMBER()、DENSE_RANK()、RANK()を使用して、上記の3つのランキングを達成できます。注意すべき点の1つは、前の関数名を複製しないでください。エラーが報告されます。以下は、これらの3つの関数によって達成されるランキングの例です。
# 三条语句对于上面三种排名
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 |
+--------+-------+-------+---------+---+
要約:
この記事では、3つの異なるシナリオでの統計的ランク付けのためのSQLを提供し、さまざまなビジネスニーズに応じて適切なランク付けスキームを選択できます。MySQL 8.0と比較して、ウィンドウ関数を使用してランク付けをより簡単に実行できることがわかりました。実際、ビジネス要件は、ここで示した例よりもはるかに複雑であり、SQLによるそのようなビジネス要件の実現はゆっくりと蓄積する必要があります。
参照: