MySQL
 Computer >> コンピューター >  >> プログラミング >> MySQL

MySQLで各グループの上位2行を取得する方法|サブクエリを使った実装例

MySQLで「各グループごとに上位2行だけを取得したい」というケースは、売上ランキングや成績一覧など、実務でもよく遭遇する課題です。GROUP BYでは各グループの集計値しか取得できないため、こうした要件にはWHERE句と相関サブクエリを組み合わせる方法が有効です。

この記事では、サンプルテーブルを作成しながら、具体的なSQLの書き方を順を追って解説します。

1. サンプルテーブルの作成

まず、名前(Name)と合計スコア(TotalScores)を持つテーブルを作成します。

mysql> create table selectTop2FromEachGroup
-> (
-> Name varchar(20),
-> TotalScores int
-> );
Query OK, 0 rows affected (0.80 sec)

2. テストデータの挿入

次に、INSERT文でレコードを挿入します。「John」と「Carol」の2人について、それぞれ3件ずつのスコアを登録します。

mysql> insert into selectTop2FromEachGroup values('John',32);
Query OK, 1 row affected (0.38 sec)
mysql> insert into selectTop2FromEachGroup values('John',33);
Query OK, 1 row affected (0.21 sec)
mysql> insert into selectTop2FromEachGroup values('John',34);
Query OK, 1 row affected (0.17 sec)
mysql> insert into selectTop2FromEachGroup values('Carol',35);
Query OK, 1 row affected (0.17 sec)
mysql> insert into selectTop2FromEachGroup values('Carol',36);
Query OK, 1 row affected (0.14 sec)
mysql> insert into selectTop2FromEachGroup values('Carol',37);
Query OK, 1 row affected (0.15 sec)

SELECT文で全レコードを確認してみましょう。

mysql> select *from selectTop2FromEachGroup;

実行結果は以下の通りです。

+-------+-------------+
| Name | TotalScores |
+-------+-------------+
| John | 32 |
| John | 33 |
| John | 34 |
| Carol | 35 |
| Carol | 36 |
| Carol | 37 |
+-------+-------------+
6 rows in set (0.00 sec)

3. 各グループの上位2行を取得するクエリ

ここからが本題です。WHERE句の中で相関サブクエリを使用し、「同じ名前のグループ内で、自分以上のスコアを持つ行が2行以下であるもの」だけを抽出します。

mysql> select *from selectTop2FromEachGroup tbl
-> where
-> (
-> SELECT COUNT(*)
-> FROM selectTop2FromEachGroup tbl1
-> WHERE tbl1.Name = tbl.Name AND
-> tbl1.TotalScores >= tbl.TotalScores
-> ) <= 2 ;

実行結果は以下の通りです。

+-------+-------------+
| Name | TotalScores |
+-------+-------------+
| John | 33 |
| John | 34 |
| Carol | 36 |
| Carol | 37 |
+-------+-------------+
4 rows in set (0.06 sec)

4. クエリの仕組みを解説

このクエリのポイントは、外側の各行に対してサブクエリが実行される「相関サブクエリ」として動作している点です。

  • tbl1.Name = tbl.Name:同じグループ(ここでは同じ名前)の行だけを比較対象に限定します。
  • tbl1.TotalScores >= tbl.TotalScores:自分自身を含め、自分以上のスコアを持つ行数をカウントします。
  • COUNT(*) <= 2:そのカウントが2以下、つまり「上位2位以内に入っている行」のみを抽出します。

この条件により、Johnグループでは34点・33点、Carolグループでは37点・36点という、それぞれ上位2件のレコードが正しく取得できています。

補足:MySQL 8.0以降ならウィンドウ関数も使える

MySQL 8.0以降を使用している場合は、ROW_NUMBER()などのウィンドウ関数を使うと、より直感的に記述できます。

SELECT Name, TotalScores
FROM (
SELECT Name, TotalScores,
ROW_NUMBER() OVER (PARTITION BY Name ORDER BY TotalScores DESC) AS rn
FROM selectTop2FromEachGroup
) AS ranked
WHERE rn <= 2;

大量データを扱う場合や可読性を重視する場合は、こちらの方法も検討するとよいでしょう。

  1. MySQLで再帰的なSELECTクエリを実行する方法【初心者向けに具体例つきで解説】

    MySQLでは、ユーザー定義変数(セッション変数)を活用することで、再帰的なSELECTクエリを実現できます。本記事では、実際にテーブルを作成し、データを挿入したうえで、再帰的なSELECTの具体的な書き方を順を追って解説します。 1. サンプルテーブルの作成 まず、再帰的なSELECTを試すためのサンプルテーブルを作成します。テーブルの作成にはCREATEコマンドを使用します。 mysql> CREATE table tblSelectDemo - > ( - > id int, - > name varchar(100) - > );

  2. MySQLでテーブルの最後の10件のレコードを取得する方法

    MySQLでテーブルの末尾にある10行(最新の10件)を取得したい場合は、SELECT文とLIMIT句を組み合わせたサブクエリを利用します。この記事では、実際にテーブルを作成し、データを挿入しながら、最後の10件を取得するクエリの手順を具体的に解説します。 1. サンプルテーブルの作成 まず、動作確認用のテーブルを作成します。 mysql> create table Last10RecordsDemo -> ( -> id int, -> name varchar(100) -> ); Query OK, 0 rows affected