目次
1.はじめに:MySQL Window関数とは?
MySQL 8.0から利用可能になったWindow関数は、データ分析の強力な味方となります。OracleやPostgreSQL、SQL Serverといった他のデータベースシステムでも利用されているこの関数は、「難しそう」「どう使えば良いかわからない」といったイメージを持たれる方もいるかもしれません。
しかし、Window関数は集計関数と組み合わせて使うことで、これまで複雑なSQL文でしか実現できなかった集計や分析を、よりシンプルかつ効率的に行うことを可能にします。
Window関数でできることの例
|
集計関数 |
Window関数としての利用 |
|
SUM |
累計値の計算 |
|
AVG |
平均値の計算 |
|
COUNT |
件数の計算 |
|
MAX |
最大値の取得 |
|
MIN |
最小値の取得 |
このように、従来の集計関数を「ウィンドウ」という概念で捉え直すことで、行ごとに集計値や順位などを付与した結果を得られるようになります。本記事では、このWindow関数の基本的な構文から、具体的なユースケース、そして実践的な活用方法までを分かりやすく解説していきます。
2.Window関数を理解するための基礎知識
Window関数は、データ分析において非常に強力な機能ですが、その仕組みを理解するにはいくつか基礎知識を押さえる必要があります。まず、集計関数との違いを明確にしましょう。
集計関数とWindow関数の違い
|
項目 |
集計関数 |
Window関数 |
|
処理結果のイメージ |
グループごとに1行に集約される |
元のレコードを保持したまま、各行に計算結果が付与される |
|
例 |
|
|
このように、Window関数は集計関数と異なり、元のレコード数を保ったまま分析結果を付与できる点が大きな特徴です。
次に、Window関数の基本的な構文を見てみましょう。
関数名() OVER (
[PARTITION BY 列名1, 列名2, ...] -- グループ化
[ORDER BY 列名1 [ASC|DESC], ...] -- 順序付け
[フレーム句] -- 対象範囲の指定
)
-
OVER句: Window関数を使用することを宣言します。
-
PARTITION BY句: データをどの単位で分割して集計・分析するかを指定します。例えば、カテゴリごとに売上を集計したい場合などに使用します。
-
ORDER BY句: 各パーティション(分割されたグループ)内でのレコードの順序を指定します。累計値やランキング計算などで重要になります。
-
フレーム句:
ORDER BY句で指定された順序に基づき、各行で計算に使用する範囲(ウィンドウフレーム)をさらに細かく指定します。
これらの要素を理解することで、Window関数を効果的に活用できるようになります。
(1)集計関数との違いを理解する
MySQLのWindow関数を理解する上で、まず従来の集計関数との違いを把握することが重要です。集計関数は、複数の行をまとめて1つの結果を返す関数ですが、Window関数は、指定した範囲(ウィンドウ)内の行に対して個別の計算を行い、その結果を各行に紐づけて返します。
例えば、ある商品の売上データを集計する場合を考えてみましょう。
|
商品名 |
売上 |
|
A |
100 |
|
B |
150 |
|
C |
120 |
集計関数(SUM)を使うと、全商品の合計売上は370となります。
|
合計売上 |
|
370 |
一方、Window関数(SUM() OVER())を使うと、各商品行に合計売上を付与できます。
|
商品名 |
売上 |
合計売上 |
|
A |
100 |
370 |
|
B |
150 |
370 |
|
C |
120 |
370 |
このように、Window関数は集計結果を個々の行に展開するため、集計前の行データを保持したまま、集計値や順位などを参照したい場合に非常に有効です。
集計関数の処理結果のイメージ
集計関数は、指定されたグループ全体に対して1つの結果を返します。例えば、以下のようなテーブルで「部署ごとの平均給与」を計算する場合を考えてみましょう。
|
従業員ID |
部署 |
給与 |
|
1 |
営業 |
50000 |
|
2 |
営業 |
60000 |
|
3 |
開発 |
70000 |
|
4 |
開発 |
80000 |
|
5 |
営業 |
55000 |
この場合、AVG(給与)といった集計関数を使うと、以下のように各部署から1つの平均値が算出されます。
|
部署 |
平均給与 |
|
営業 |
55000 |
|
開発 |
75000 |
つまり、集計関数は行ごとに個別の結果を返すのではなく、グループ全体を要約した結果を返します。この点は、後述するWindow関数が各行に対して計算結果を返すのとは大きく異なります。
Window関数の処理結果のイメージ
集計関数とWindow関数では、集計結果の出力形式が異なります。集計関数では、グループごとに1行の集計結果が出力されるのに対し、Window関数では、元のテーブルの各行に対して計算結果が付与されます。
例えば、以下のような売上データがあったとします。
|
日付 |
売上 |
|
2023-01-01 |
100 |
|
2023-01-02 |
150 |
|
2023-01-03 |
120 |
このデータに対して、日ごとの累計売上をWindow関数(SUM OVER (ORDER BY 日付))で計算すると、以下のようになります。
|
日付 |
売上 |
累計売上 |
|
2023-01-01 |
100 |
100 |
|
2023-01-02 |
150 |
250 |
|
2023-01-03 |
120 |
370 |
このように、Window関数を使用すると、元のデータの行を維持したまま、各行に対応する集計値などを表示できるため、より詳細な分析が可能になります。
(2)Window関数の基本的な構文
Window関数は、その名の通り「ウィンドウ(窓)」を定義し、そのウィンドウ内のデータに対して集計や計算を行う機能です。このウィンドウを定義するために、OVER句が不可欠となります。OVER句は、Window関数が適用されるべき行の集合を定義する役割を担います。
OVER句の中では、主に以下の3つの句を組み合わせてウィンドウを細かく指定できます。
-
PARTITION BY句: データを特定のグループに分割します。例えば、部署ごとや商品カテゴリごとに集計したい場合に使用します。これにより、各パーティション(グループ)内で独立して計算が実行されます。
-
ORDER BY句: 各パーティション内での行の順序を定義します。累計計算やランキング付けなど、順序が重要な場合に必須となります。
-
フレーム(ウィンドウフレーム): 各行に対して、どの範囲のデータ(フレーム)を計算対象とするかを指定します。デフォルトでは、
PARTITION BYで定義されたパーティション全体や、ORDER BYで定義された順序に基づいた特定の範囲が対象となります。
これらの句を組み合わせることで、柔軟かつ強力なデータ集計・分析が可能になります。
OVER句の役割
Window関数は、集計関数とは異なり、クエリの結果セット全体または特定のパーティション(グループ)内の各行に対して計算を行います。このWindow関数を適用する範囲を指定するのがOVER句です。OVER句を指定することで、その関数がどの範囲のデータに対して、どのような順序で、どのように集計・計算を行うかを定義します。
OVER句は、Window関数を呼び出す際に必須となる句です。この句がないと、Window関数は正しく機能しません。
OVER句の主な役割は以下の通りです。
|
役割 |
説明 |
|
計算範囲の定義 |
関数が適用される行の集合(ウィンドウ)を定義します。 |
|
パーティショニング |
|
|
順序付け |
|
|
フレーム指定 |
ウィンドウフレーム( |
OVER句を適切に定義することで、複雑なデータ分析も効率的に行うことが可能になります。
PARTITION BY句:グループ化の指定
Window関数では、OVER句の中にPARTITION BY句を指定することで、データを特定のグループに分割し、各グループ内でWindow関数を適用できます。これにより、全体のデータに対して一度に処理を行うのではなく、グループごとの分析が可能になります。
例えば、以下のようなデータがあったとします。
|
カテゴリ |
商品名 |
売上 |
|
食品 |
りんご |
100 |
|
食品 |
みかん |
150 |
|
雑貨 |
ペン |
50 |
|
雑貨 |
ノート |
70 |
このデータに対して、カテゴリごとに売上の累計を計算したい場合、PARTITION BY句で「カテゴリ」を指定します。
SELECT
カテゴリ,
商品名,
売上,
SUM(売上) OVER (PARTITION BY カテゴリ) AS カテゴリ別総売上
FROM
商品売上テーブル;
このSQLを実行すると、各カテゴリ内で売上の合計が計算され、以下のような結果が得られます。
|
カテゴリ |
商品名 |
売上 |
カテゴリ別総売上 |
|
食品 |
りんご |
100 |
250 |
|
食品 |
みかん |
150 |
250 |
|
雑貨 |
ペン |
50 |
120 |
|
雑貨 |
ノート |
70 |
120 |
このように、PARTITION BY句を用いることで、データ分析の粒度を細かく制御し、より目的に合った集計や分析を行うことができます。
ORDER BY句:順序の指定
Window関数は、指定した「ウィンドウ(範囲)」内のデータに対して計算を行います。このウィンドウの範囲をどのように設定するかは、OVER句の中で指定します。OVER句には、ウィンドウをグループ化するためのPARTITION BY句と、ウィンドウ内のデータの順序を指定するためのORDER BY句があります。
ORDER BY句は、ウィンドウ関数が処理するレコードの順序を決定するために不可欠です。例えば、累計値を計算する場合、どの順序で値を合計していくかが重要になります。ORDER BY句を指定しない場合、ウィンドウ内のレコードの順序は不定となり、期待通りの結果が得られない可能性があります。
|
句 |
役割 |
|
|
ウィンドウをグループ化します。 |
|
|
ウィンドウ内のレコードの順序を指定します。 |
ORDER BY句で指定した順序に基づいて、SUMやAVGのような集計関数が適用されます。また、RANKやDENSE_RANKのような順位付け関数においても、この順序が順位決定の基準となります。
フレーム(ウィンドウフレーム):対象範囲の指定
Window関数では、OVER句で指定したパーティション(グループ)の中で、どの行を対象とするかをさらに細かく定義できます。この対象範囲のことを「フレーム」と呼びます。フレームを指定しない場合、MySQLはデフォルトのフレーム規則に従います。
フレームには主に以下の種類があります。
-
ROWフレーム: 現在の行を基準に、前後の行数で範囲を指定します。
-
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: パーティションの最初から現在の行まで -
ROWS BETWEEN 1 FOLLOWING AND 2 FOLLOWING: 現在の行の1つ後から2つ後まで
-
-
RANGEフレーム: 現在の行の値(ORDER BY句で指定した列の値)を基準に、相対的な範囲を指定します。
-
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW: パーティションの最初から現在の行と同じ値を持つ行まで -
RANGE BETWEEN INTERVAL '1' DAY PRECEDING AND CURRENT ROW: 現在の行から1日前までの値を持つ行まで
-
これらのフレームを適切に設定することで、例えば「月ごとの累計値」や「直近3日間の平均値」といった、より複雑で柔軟な分析が可能になります。
3.Window関数でできること:具体的なユースケースとSQL例
Window関数を活用することで、集計関数だけでは難しかった、より高度なデータ分析が可能になります。ここでは、代表的なWindow関数とその活用例をご紹介します。
(1)集計・ランキング系Window関数
-
SUM: 特定のグループ内での累計値を計算できます。例えば、月ごとの会員数の推移を把握したい場合に便利です。
SELECT month, SUM(members) OVER (ORDER BY month) AS cumulative_members FROM monthly_members; -
AVG: グループごとの平均値を計算します。
-
COUNT: グループごとの件数を計算します。
-
RANK / DENSE_RANK: 売上やアクセス数などの指標に基づいた順位付けを行います。
RANKは同順位の場合、次の順位を飛ばしますが、DENSE_RANKは順位を詰めずに連番で表示します。 -
PERCENT_RANK: 全体における相対的な順位を0から1の範囲で計算します。
-
NTILE: 全体を指定した数で分割し、各グループにどのデータが含まれるかを示します。
(2)前後データ参照系Window関数
-
LAG / LEAD: 特定の順序で並べた際の、直前または直後の行の値を取得できます。これにより、前月比や翌月比の計算が容易になります。
-
FIRST_VALUE / LAST_VALUE: ウィンドウ(指定した範囲)の最初または最後の値を取得します。
-
NTH_VALUE: ウィンドウ内のN番目の値を取得します。
これらの関数を組み合わせることで、複雑な分析もSQLで簡潔に記述できるようになります。
(1)集計・ランキング系Window関数
Window関数は、単にデータを集計するだけでなく、集計結果を元にした様々な分析を可能にします。ここでは、代表的な集計・ランキング系Window関数について解説します。
|
関数名 |
概要 |
|
SUM |
指定した範囲(ウィンドウ)内の合計値を計算します。月ごとの累計値などを求める際に便利です。 |
|
AVG |
指定した範囲内の平均値を計算します。 |
|
COUNT |
指定した範囲内のレコード数を計算します。 |
|
RANK |
ウィンドウ内での順位を計算します。同順位の場合、次の順位はスキップされます。 |
|
DENSE_RANK |
ウィンドウ内での順位を計算します。同順位の場合でも、次の順位は詰めて表示されます。 |
|
PERCENT_RANK |
ウィンドウ内での相対的な順位を0から1の間の値で計算します。 |
|
NTILE |
指定したグループ数にレコードを均等に分割し、各レコードがどのグループに属するかを示します。 |
例えば、SUM関数とPARTITION BY句、ORDER BY句を組み合わせることで、顧客IDごとに会員登録月を基準とした累計会員数を容易に計算できます。このように、Window関数を活用することで、複雑な集計やランキング処理を簡潔かつ効率的に実行することが可能です。
SUM:累計値の計算
Window関数の中でも特に便利なのが、累計値を計算できるSUM関数です。これは、指定したグループ内での合計値を、各行に対して計算します。例えば、会員登録のデータがある場合、月ごとの累計会員数を簡単に求めることができます。
従来のSQLでは、自己結合などの複雑な処理が必要でしたが、Window関数を使えば、よりシンプルに記述できます。
SELECT
member_id,
registration_date,
SUM(1) OVER (ORDER BY registration_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cumulative_members
FROM
members;
このSQL文では、registration_date の昇順で会員を並べ、各行でそれ以前の行の数を合計しています。これにより、日付が新しいほど、それまでに登録した会員の総数がcumulative_membersとして表示されます。
|
registration_date |
cumulative_members |
|
2023-01-01 |
1 |
|
2023-01-15 |
2 |
|
2023-02-10 |
3 |
|
2023-03-05 |
4 |
このように、SUM関数とOVER句を組み合わせることで、データ分析において頻繁に必要となる累計値の算出が、直感的かつ効率的に行えるようになります。
月ごとの累計会員数を計算する例
会員登録のデータがある場合、月ごとの累計会員数を計算することで、会員数の増加傾向を把握することができます。Window関数を利用すると、この累計値を簡単に計算できます。
具体的には、SUM()関数とOVER()句を組み合わせて使用します。OVER()句内でPARTITION BY句は使用せず、ORDER BY句で会員登録日(created_at)を昇順に指定します。これにより、各行に対して、その行までの会員登録数の合計を計算できます。
SELECT
DATE_FORMAT(created_at, '%Y-%m') AS registration_month,
COUNT(*) AS monthly_registrations,
SUM(COUNT(*)) OVER (ORDER BY DATE_FORMAT(created_at, '%Y-%m')) AS cumulative_registrations
FROM
members
GROUP BY
registration_month
ORDER BY
registration_month;
このSQL文では、まずmembersテーブルから会員登録月ごとの登録数をmonthly_registrationsとして集計します。次に、SUM(COUNT(*)) OVER (ORDER BY DATE_FORMAT(created_at, '%Y-%m'))によって、登録月順に累計会員数(cumulative_registrations)を算出しています。
このようにWindow関数を利用することで、複雑なサブクエリや自己結合を使わずに、累計値の計算を効率的に行うことが可能です。
AVG:平均値の計算
Window関数の中でも、AVG関数は指定したウィンドウ内の値の平均値を計算するために使用されます。集計関数としてのAVG関数は、グループ全体を対象に1つの結果を返しますが、Window関数としてのAVG関数は、各行に対して、その行を含むウィンドウ内の平均値を計算して返します。
例えば、月ごとの会員数を集計し、さらに各月の累計会員数と、その時点での平均会員数を算出したい場合を考えてみましょう。
|
月 |
会員数 |
累計会員数 (SUM) |
平均会員数 (AVG) |
|
1月 |
100 |
100 |
100.00 |
|
2月 |
120 |
220 |
110.00 |
|
3月 |
150 |
370 |
123.33 |
このように、Window関数を用いることで、複雑な集計を単一のクエリで効率的に行うことが可能になります。
COUNT:件数の計算
COUNT関数は、指定した条件に合致する行数を集計する際に非常に役立ちます。Window関数としてCOUNTを使用すると、集計関数のように全体の件数を取得するだけでなく、特定のグループ内での件数や、並び順における件数などを動的に取得できます。
例えば、月ごとの会員登録数を集計したい場合を考えてみましょう。従来のSQLでは、月ごとにGROUP BY句を使って集計し、さらにJOINなどを用いて累計を計算する必要がありました。しかし、Window関数を使えば、以下のように簡潔に記述できます。
SELECT
registration_date,
COUNT(*) OVER (ORDER BY registration_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS monthly_cumulative_count
FROM
members;
このSQLは、registration_dateで昇順に並べた上で、各行においてその行までの累計件数を計算しています。このように、Window関数を用いることで、複雑なサブクエリや自己結合を避け、よりシンプルで可読性の高いSQLを作成することが可能になります。
RANK:順位付け(同順位の扱い)
RANK関数は、指定した順序に基づいて各行に順位を付けるWindow関数です。例えば、テストの点数に基づいて生徒に順位を付ける場合などに活用できます。
RANK関数は、同じ値を持つ行に対して同じ順位を割り当てます。しかし、同順位の行があった場合、次の順位はスキップされます。例えば、1位が2名いた場合、次の順位は3位となります。
|
点数 |
名前 |
RANK() OVER (ORDER BY 点数 DESC) |
|
100 |
山田 |
1 |
|
100 |
佐藤 |
1 |
|
90 |
鈴木 |
3 |
|
80 |
田中 |
4 |
このように、RANK関数は同順位の発生を考慮しつつ、順序に基づいたランキングを作成する際に便利です。
DENSE_RANK:順位付け(同順位でも次の順位を詰める)
DENSE_RANK()関数は、RANK()関数と同様に、ウィンドウ内の行に順位を付けるための関数です。しかし、DENSE_RANK()の最大の特徴は、同順位が発生した場合でも、次の順位を飛ばさずに連番で付与する点にあります。
例えば、テストの点数で順位を付ける場合を考えてみましょう。
|
名前 |
点数 |
RANK() |
DENSE_RANK() |
|
山田太郎 |
90点 |
1位 |
1位 |
|
佐藤花子 |
90点 |
1位 |
1位 |
|
鈴木一郎 |
80点 |
3位 |
2位 |
|
田中次郎 |
70点 |
4位 |
3位 |
このように、DENSE_RANK()を使用すると、同順位の生徒がいても、次の順位が「2位」となり、順位が詰まることがわかります。これは、相対的な順位をより正確に把握したい場合に非常に役立ちます。
PERCENT_RANK:相対順位の計算
PERCENT_RANK関数は、現在の行の順位を、ウィンドウ内の全行数と比較して0から1の間の値で表す関数です。これにより、データセット全体における相対的な位置を把握することができます。
例えば、テストの点数において、ある生徒のPERCENT_RANKが0.75だった場合、その生徒は全体の75パーセンタイルに位置していることを意味します。これは、自分より低い点数の生徒が全体の75%いる、ということです。
|
関数名 |
説明 |
|
PERCENT_RANK |
相対順位を0から1の間の値で計算する |
計算方法:
PERCENT_RANK = (RANK - 1) / (全行数 - 1)
ここで、RANKはRANK関数で計算される順位です。
この関数を利用することで、例えば、ある商品の売上シェアや、従業員のパフォーマンスの相対的な位置づけなどを客観的に評価する際に役立ちます。集計関数や他のWindow関数と組み合わせることで、より多角的なデータ分析が可能になります。
NTILE:グループへの分割
NTILE関数は、指定したグループ数に基づいてデータを分割し、各データがどのグループに属するかを示す順位を付与するのに役立ちます。例えば、売上データを5つのグループ(クォタイル)に分割し、各売上がどのランクに位置するかを把握したい場合などに利用できます。
NTILE関数の基本的な構文は以下の通りです。
NTILE(グループ数) OVER (
[PARTITION BY 列名]
ORDER BY 列名
)
-
グループ数: データを分割したいグループの数(例:4分割なら4、10分割なら10)。
-
PARTITION BY: データを分割する基準となる列を指定します。省略すると、テーブル全体を対象とします。
-
ORDER BY: データを並べ替える基準となる列を指定します。この並び順に基づいてグループが割り当てられます。
例えば、社員の給与データを3つのグループに分割する場合、以下のようなSQL文で実現できます。
SELECT
社員名,
給与,
NTILE(3) OVER (ORDER BY 給与) AS 給与グループ
FROM
employees;
|
社員名 |
給与 |
給与グループ |
|
山田太郎 |
500,000 |
3 |
|
佐藤花子 |
450,000 |
2 |
|
鈴木一郎 |
400,000 |
2 |
|
高橋次郎 |
350,000 |
1 |
|
田中三郎 |
300,000 |
1 |
この関数を使うことで、データ全体を均等に分割し、各レコードの相対的な位置を把握することが容易になります。
(2)前後データ参照系Window関数
Window関数は、集計やランキングだけでなく、前後の行を参照する機能も提供します。これにより、時系列データ分析などで前月比や次月比を計算する際に、非常に強力なツールとなります。
-
LAG関数: 現在の行の「一つ前の行」の値を参照できます。例えば、売上データにおいて、前月の売上を計算する際に利用できます。
-
LEAD関数: 現在の行の「一つ後の行」の値を参照できます。例えば、来月の売上予測の参考にするために、次の月の売上データを参照する際に役立ちます。
-
FIRST_VALUE関数: ウィンドウ(指定した範囲)の「最初の行」の値を返します。
-
LAST_VALUE関数: ウィンドウの「最後の行」の値を返します。
-
NTH_VALUE関数: ウィンドウ内の「N番目の行」の値を返します。
これらの関数を組み合わせることで、より複雑なデータ分析が可能になります。
|
関数名 |
説明 |
|
LAG(expr, N) |
現在行からN個前の行のexprの値を取得 |
|
LEAD(expr, N) |
現在行からN個後の行のexprの値を取得 |
|
FIRST_VALUE(expr) |
ウィンドウの最初の行のexprの値を取得 |
|
LAST_VALUE(expr) |
ウィンドウの最後の行のexprの値を取得 |
|
NTH_VALUE(expr, N) |
ウィンドウのN番目の行のexprの値を取得 |
LAG:前の行の値を取得
LAG関数は、現在の行よりも前の行にあるデータを取得する際に便利なWindow関数です。特定の順序で並んだデータの中で、一つ前のレコードの値を参照したい場合に活用できます。
例えば、以下のような会員登録履歴のデータがあったとします。
|
member_id |
registration_date |
|
1 |
2023-01-15 |
|
2 |
2023-02-10 |
|
1 |
2023-03-20 |
|
3 |
2023-04-05 |
|
2 |
2023-05-12 |
このデータに対して、LAG関数を使って各会員の2回目の登録日を計算したい場合を考えます。この際、PARTITION BY member_idで会員ごとにグループ化し、ORDER BY registration_dateで登録日の昇順に並べ替えることで、各会員の登録履歴を時系列で扱えます。
SELECT
member_id,
registration_date,
LAG(registration_date, 1, NULL) OVER (PARTITION BY member_id ORDER BY registration_date) AS previous_registration_date
FROM
members_history;
このSQLを実行すると、各会員の「一つ前の登録日」が取得できます。
|
member_id |
registration_date |
previous_registration_date |
|
1 |
2023-01-15 |
NULL |
|
1 |
2023-03-20 |
2023-01-15 |
|
2 |
2023-02-10 |
NULL |
|
2 |
2023-05-12 |
2023-02-10 |
|
3 |
2023-04-05 |
NULL |
このように、LAG関数は時系列データ分析や、前回の値との差分を計算する際などに非常に役立ちます。
LEAD:次の行の値を取得
「LEAD」関数は、現在の行から指定した行数だけ後の行の値を参照したい場合に非常に便利です。例えば、ある月の売上データがあったときに、翌月の売上を同じ行で確認したいといったケースで活用できます。
LEAD関数は、以下のような構文で使用します。
LEAD(expression [, offset [, default]]) OVER (
[PARTITION BY partition_expression, ...]
ORDER BY sort_expression [ASC | DESC], ...
)
-
expression: 取得したい値が含まれる列名や計算式を指定します。 -
offset: 現在の行から何行後の値を取得するかを指定します。省略した場合はデフォルトで「1」となり、直後の行の値が取得できます。 -
default: offsetで指定した行数が存在しない場合に返される値を指定します。
例えば、以下のような会員登録履歴のテーブルがあったとします。
|
member_id |
registration_date |
|
1 |
2023-01-10 |
|
2 |
2023-01-15 |
|
3 |
2023-02-01 |
このテーブルで、各会員の次の会員の登録日を確認したい場合は、LEAD関数を使って以下のように記述できます。
SELECT
member_id,
registration_date,
LEAD(registration_date, 1, '9999-12-31') OVER (ORDER BY registration_date) AS next_registration_date
FROM
members;
これにより、各会員の登録日と、その次の会員の登録日を一覧で表示できます。最後の会員については、offsetで指定した行数が存在しないため、defaultで指定した「9999-12-31」が表示されます。
FIRST_VALUE:ウィンドウの最初の値を取得
FIRST_VALUE関数は、指定したウィンドウフレーム(対象範囲)の最初の行の値を返します。LAG関数やLEAD関数のように前後関係を参照するのではなく、常にウィンドウの「始まり」の値を取得したい場合に便利です。
例えば、月ごとの会員数推移を分析する際に、各月の会員数のうち、その期間の最初の会員数を把握したいといったケースが考えられます。
|
処理内容 |
概要 |
|
FIRST_VALUE関数 |
ウィンドウフレームの最初の行の値を返す |
具体的な利用シーン例
-
累計値の基準値として: 月ごとの累計会員数を計算する際に、その月の最初の会員数を基準として、増加分を把握したい場合。
-
特定期間の初期値の把握: ある期間のデータの中で、その期間が始まった時点での値を確認したい場合。
この関数を使うことで、データセット内の各行に対して、その行が属するウィンドウの初期値を容易に参照できるようになります。
LAST_VALUE:ウィンドウの最後の値を取得
LAST_VALUE関数は、指定したウィンドウフレーム(対象範囲)の最後の行の値を返します。これは、LAGやLEADのように前後関係でデータを取得する関数とは異なり、フレームの終端にあるデータにアクセスしたい場合に役立ちます。
例えば、各部署の従業員リストがあり、それぞれの部署内で最後に登録された従業員の情報を取得したい場合などに活用できます。
構文例:
LAST_VALUE(column_name) OVER (
[PARTITION BY partition_expression]
[ORDER BY order_expression]
[frame_clause]
)
ここで重要なのは、frame_clause(フレーム句)の設定です。デフォルトだと現在行までしか見ないので、最後を取りたいならフレームを広げる必要があります。例えば、ORDER BY句で並べ替えた際の「最後の行」を取得したい場合は、フレーム句を明示的に指定する必要があります。
フレーム句の例:
-
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING: パーティション全体の行を対象とします。 -
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING: 現在の行からパーティションの最後の行までを対象とします。
これらの設定を適切に行うことで、LAST_VALUE関数を効果的に利用し、データ分析の幅を広げることができます。
NTH_VALUE:ウィンドウのN番目の値を取得
NTH_VALUE関数は、指定したウィンドウフレーム内でN番目の行の値を取得できる便利な関数です。例えば、各部署の売上データにおいて、2番目に高い売上や3番目に低い売上などを抽出したい場合に活用できます。
基本的な構文
NTH_VALUE(column, n) OVER (
[PARTITION BY partition_expression]
[ORDER BY sort_expression]
[frame_clause]
)
-
column: 値を取得したい列名 -
n: 取得したい値の順番(例: 1, 2, 3...)
具体的な利用シーン
例えば、以下のような売上テーブルがあったとします。
|
department |
sales |
|
A |
100 |
|
A |
200 |
|
A |
150 |
|
B |
300 |
|
B |
250 |
|
B |
350 |
このテーブルで、各部署の売上を降順に並べた際の2番目の値を取得したい場合、以下のようなSQL文で実現できます。
SELECT
department,
sales,
NTH_VALUE(sales, 2) OVER (PARTITION BY department ORDER BY sales DESC) AS second_highest_sales
FROM
sales_table;
このクエリを実行すると、各部署で2番目に高い売上額がsecond_highest_sales列に表示されます。
この関数は、LAG関数やLEAD関数のように前後関係だけでなく、ウィンドウ内の任意の順番の値を取得できる点で強力です。
4.実践!Window関数を使ったSQLチューニング
Window関数を理解したところで、実際にSQLチューニングの観点からその効果を見ていきましょう。
(1)Window関数を使わない場合との比較
Window関数を導入することで、SQLの記述方法や可読性が大きく向上します。従来のSQLでは、集計や順位付けを行うために、自己結合やサブクエリを多用する必要がありました。
例えば、各部署の従業員ごとの給与の累計を計算する場合、Window関数を使わない場合は以下のような複雑なクエリになりがちです。
SELECT
employee_name,
department,
salary,
(SELECT SUM(salary)
FROM employees e2
WHERE e2.department = e1.department AND e2.employee_id <= e1.employee_id) AS cumulative_salary
FROM
employees e1;
一方、Window関数を利用すると、同じ結果をより簡潔かつ直感的に記述できます。
SELECT
employee_name,
department,
salary,
SUM(salary) OVER (PARTITION BY department ORDER BY employee_id) AS cumulative_salary
FROM
employees;
このように、Window関数を用いることで、SQL文が短く、意図が把握しやすくなり、メンテナンス性も向上します。特に、複雑なデータ分析や集計処理において、その威力を発揮します。
(2)名前付きWindow(Named Window)の活用
Window関数の定義は、OVER句内にPARTITION BYやORDER BYなどを記述することで行われます。しかし、複数のWindow関数を使用する場合、同じ定義を繰り返し記述することになり、クエリが冗長で読みにくくなることがあります。
そこでMySQL 8.0では、「名前付きWindow(Named Window)」という機能が導入されました。これは、WINDOW句を用いてWindowの定義に名前を付け、それをOVER句で参照できるようにするものです。
例えば、以下のように WINDOW 句を使って定義と参照を記述します。
SELECT
employee_name,
department,
salary,
RANK() OVER my_window AS department_rank
FROM employees
-- ▼ここで定義します(必ずFROM句などの後ろに書きます)
WINDOW
my_window AS (PARTITION BY department ORDER BY salary DESC);
名前付きWindowを活用することで、以下のようなメリットがあります。
-
クエリの簡潔化: 同じWindow定義の繰り返しを避けることができます。
-
可読性の向上: 複雑なクエリでも、Windowの定義が整理され、理解しやすくなります。
-
保守性の向上: Window定義の変更が必要になった場合、
WINDOW句の定義箇所のみを修正すればよくなります。
特に、複数のWindow関数を定義したり、複雑なパーティションや順序を指定したりする場合には、名前付きWindowの活用が非常に有効です。
5.まとめ:Window関数を使いこなしてデータ分析を加速しよう
本記事では、MySQLのWindow関数について、その基本から実践的な使い方までを解説しました。Window関数は、集計関数とは異なり、クエリ結果の各行に対して、関連する行の集合(ウィンドウ)に基づいた計算を行うことができます。
Window関数の主なメリット
|
メリット |
詳細 |
|
集計・分析の効率化 |
累計値、順位付け、前後データ参照などが簡潔に記述可能 |
|
SQLの可読性向上 |
複雑なサブクエリや自己結合が不要になり、SQL文がスッキリする |
|
データ分析の加速 |
より高度で多様な分析が容易になり、ビジネスインサイトの発見を促進する |
OVER句、PARTITION BY句、ORDER BY句を理解し、SUM、AVG、RANK、LAG、LEADといった多様なWindow関数を使いこなすことで、これまで煩雑だったデータ集計や分析が格段に効率化されます。特に、名前付きWindowを活用することで、複雑なクエリの管理も容易になります。
ぜひ、本記事で学んだWindow関数の知識を、日々のデータ分析業務に活用し、より迅速で深い洞察を得るためにお役立てください。