MySQL8.0のWindow関数を分かりやすく紹介する(3.順位・連番の付け方(発展編))

MySQL 8.0
目次

はじめに

MySQL8.0がリリースされたことにより、Window関数の利用が可能になりました。
Oracle、PostgreSQL、SQL Server等でも利用できる関数ですが、「難しそう」、「どういう場合に使えば良いか分からない」というイメージを持たれている方もいると思いますので、この記事ではシンプルにどのようなことができるのかを紹介していきます。

過去の記事でもWindow関数について分かりやすく記載しておりますので、確認されていない方は是非こちらの記事もご参照ください。

MySQL8.0のWindow関数を分かりやすく紹介する(1.「集計関数×Window関数」のコラボ)
MySQL8.0のWindow関数を分かりやすく紹介する(2.順位・連番の付け方(基本編))

今回は第三弾として「順位・連番の付け方(発展編)」というお題で記載します。

データ準備

以下のように、好きな食べ物の情報と投票数(votes)を管理する検証用テーブルを作成します。

補足として分類(category)については大まかなデータを登録しています。
例えばラーメン(Ramen)は「中国人シェフによって日本で生み出された料理であるため、純粋な中華料理ではなく日本人のために作られた中華風の料理」のようです。

分類としては、「和食 or 中華」で人によって意見が分かれるかもしれませんが、あまり深く考えずに「和食(Japanese food)」としておりますので、ご了承ください。

「グループ単位」で順位を付けてみる

前回の記事でRANK、DENSE_RANK関数を利用した順位付けの方法を紹介しましたが、ワンポイントアレンジして「分類(category)」単位で順位付けする方法を紹介します。

ケース1:RANK関数で順位付け

まずはRANK関数を利用するケースです。
ここでのポイントはPARTITION BY categoryと指定することによって、「分類(category)」単位で順位付けすることができます。

SQLの中で「グループ分けの機能」がある句として、「GROUP BY」と「PARTITION BY」の2つがあります。
簡単に機能面の説明をすると、どちらもデータをグループ分けしますが、「GROUP BY」は「(グループ分け後に)重複データを1行にまとめる」という処理が行われます。

file

もしRANK関数を利用しない場合は、前回の記事でも紹介したSELECT句にサブクエリを記述するような方法がありますが、頭を柔らかくしてSQLを考える必要があります。

ケース2:DENSE_RANK関数で順位付け

次にDENSE_RANK関数を利用するケースです。
ケース1のRANK関数のSQLと比較して頂けると分かると思いますが、PARTITION BY categoryを指定する点、考え方は全く一緒です。

参考として、DENSE_RANK関数を利用しない場合のSQLも記載します。

「2位、3位じゃだめなんですか?」

データを取得するときに「1位のデータだけを取得してほしい!」と聞くことはあったとしても、
なかなか「2位、3位のデータだけを取得してほしい」とは聞かないと思います。

1位のデータを贔屓するわけではありませんが、
「分類(category)」単位で「1位のデータ」、「1~3位のデータ」を取得する方法を記載します。

2位、3位のデータだけを取得したい場合でも簡単に流用できますので、是非ご参考にしてください。

ケース1:「1位のデータ」の取得方法

今回はRANK関数を使用して順位付けします。
まずは失敗例から紹介します。

これまでのSQLの例を見返してみるとSELECT句でrankingという列別名を付けており、
ORDER BY句では、rankingを利用して昇順にソートしていました。

しかしWHERE句でrankingを利用しようとすると、以下のようにSQLを記述するとエラーになります。
なぜエラーになるのでしょうか?

結論としては「SELECT文には一般的な実行順序」が存在するので、
SELECT句で定義したrankingという列別名は後続のORDER BY句では利用できますが、
WHERE句はSELECT句の前で評価されるので、rankingという列別名を利用することができません。

ここでは実行順序の詳細についてはまた別の機会で紹介したいと思いますので割愛しますが、
今回の例ではSELECT、FROM、WHERE、ORDER BY句の4つの句が存在し、「FROM→WHERE→SELECT→ORDER BY」の順番で処理が行われます。

 
では、どうしたらWHERE句を利用することができるようになるかというと・・・
FROM句のサブクエリで「分類(category)」単位で順位付けしたSELECT文を記述します。

FROM句では、サブクエリの結果を1つのテーブル(仮想のテーブル)として扱えるため、
tmpテーブル(という名の仮想のテーブル)の全データをターゲットとし、WHERE句で絞り込むことができます。
最終的なSQLと実行結果は以下のようになります。

ケース2:「1~3位のデータ」の取得方法

ケース1と同様にRANK関数を使用して順位付けします。
基本的な考え方も同じですので、後はrankingの値が「3」以下のデータを取得するようにWHERE句の絞り込み条件だけ調整します。

「不正ではありません。並び順を変えているだけですよ!」

以下は「和食(Japanese food)」を除いた「投票数(votes)」に対してのベストランキングを作成しています。

RANK関数の結果(ranking)、DENSE_RANK関数の結果(dense_ranking)に注目すると・・・
1行目の焼肉と2行目のアイスクリームは同順位ですが、並び順を見ると焼肉が勝っているように見えます。

順位を変更するという不正をせずに並び替えたい場合は、
ORDER BY句を活用し、例えばranking, food_kana ASCとすることによって実現できます。

実際に実行してみると以下のように、
RANK関数の結果(ranking)、DENSE_RANK関数の結果(dense_ranking)は想定通りの並び順になりました。

さらに連番を表すROW_NUMBER関数の結果(serial_number)の「1」と「2」を入れ替えたい場合は、
OVER句内の並び順をORDER BY votes DESC, food_kana ASCとする方法があります。

まとめ

今回はWindow関数を利用して、順位・連番の付け方の発展編を紹介しました。
基本編からアレンジを少し加えただけでも、できる範囲が広がっていることを理解して頂き、今後に活用して頂けますと幸いです。

スマートスタイルTECHブログについて

スマートスタイルTECHブログでは、日頃MySQLのサポート業務に従事している有資格者で構成された技術サポートチームがMySQLに関する技術情報を発信しています。データベースのお困りごとはお気軽にご相談下さい。

よかったらシェアしてね!
  • URLをコピーしました!
目次