SQL Everyone学習する
SQL EveryoneSQL Everyone
  • トップ
  • マイページ
  • データベース確認
  • 学習ページ
    • SQL概要
    • SQL実行環境構築
    • SQL文法基礎
    • SQL実践問題(初級)
    • SQL実践問題(中級)
    • SQL実践問題(上級)
SQL EveryoneSQL Everyone
  • トップ
  • マイページ
  • データベース確認
  • 学習ページ
    • SQL概要
    • SQL実行環境構築
    • SQL文法基礎
    • SQL実践問題(初級)
    • SQL実践問題(中級)
    • SQL実践問題(上級)
料金プランお問い合わせセミナー記事
プライバシーポリシー利用規約特定商取引法表記

© 2026 SQL Everyone

読み込み中...
SQL文法基礎8/8
No.8

【SQL文法基礎8】複雑な集計をする〜 WINDOW関数 〜

これまでの章では、GROUP BYを使ってデータを集約する方法を学んできました。GROUP BYはとても便利な機能ですが、「集計した値を見ながら、元の行ごとの情報も同時に確認したい」という場面では、うまく使えないことがあります。本章では、そのような複雑な集計を可能にするWINDOW関数について学んでいきましょう。

WINDOW関数とは

通常の集計との違い

WINDOW関数を理解するために、まずGROUP BYを使った集計と何が違うのかを確認しましょう。

例えば、ユーザーごとの2023年の購入金額を集計することを考えます。GROUP BYを使うと、SQL10-1のように1ユーザー1行になった集計結果を取得することができます。

★ハンズオン〜SQLを実行してみよう〜

・SQL10-1

</>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SELECT
    o.user_id,
    SUM(p.price) AS total_price
FROM
    orders AS o
    LEFT JOIN products AS p ON o.product_id = p.product_id
WHERE
    o.user_id IS NOT NULL
    AND o.order_date BETWEEN '2023-01-01' AND '2023-12-31'
    AND o.is_canceled = 0
GROUP BY
    o.user_id
ORDER BY
    total_price DESC
LIMIT 10

・表10-1:SQL10-1の実行結果

user_id

total_price

A000276

24712867

A000922

12462096

A000026

8425735

A000496

6035498

A000799

5676461

A000861

4527191

A000195

3662527

A000028

3008086

A000373

2618627

A000212

2453066

・・・

・・・

ここで「購入金額が多い順に順位をつけて表示したい」という要件が加わったとします。GROUP BYの結果には順位の情報がないため、この集計結果に対してさらにSQLを実行する必要があります。第9章で学んだサブクエリやWITH句を使えば実現できますが、シンプルな要件に対してSQLが複雑になってしまいます。

このような「各行に対して集計した値を付け加えたい」という場面で役に立つのがWINDOW関数です。WINDOW関数は、GROUP BYのように行をまとめることなく、各行を保ちながら集計や計算を行うことができる機能です。

・図10-2:GROUP BYとWINDOW関数の違い

GROUP BYとWINDOW関数の違い
GROUP BYとWINDOW関数の違い

WINDOW関数の基本構文

WINDOW関数は、SELECTの中で次のように記述します。関数名()のあとにOVERを続け、その中にどの範囲の行を対象にするかを指定します。このOVERの中で指定する範囲のことを「ウィンドウ(窓)」と呼ぶことから、WINDOW関数という名前がついています。

・WINDOW関数の基本構文

</>
1
2
3
4
5
SELECT
    カラム名,
    関数名() OVER (ORDER BY カラム名) AS 別名
FROM
    テーブル名

OVERの中では、主に次の2つを指定します。

  • ORDER BY:どの順番で計算するかを指定します。順位をつけたいときはORDER BYで並び替えの基準を設定します
  • PARTITION BY:グループを分けて、グループごとに計算する場合に指定します(後述)

順位をつける〜RANK関数〜

RANK()の使い方

WINDOW関数の中でも、最もよく使われるのがRANK関数です。RANK()はデータに順位をつけることができる関数です。先ほどSQL10-1で集計したユーザーごとの購入金額に、RANK関数で順位をつけたのがSQL10-2になります。まずはSQL10-2を実行して出力結果を確認してみましょう。

★ハンズオン〜SQLを実行してみよう〜

・SQL10-2

</>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SELECT
    o.user_id,
    SUM(p.price) AS total_price,
    RANK() OVER (ORDER BY SUM(p.price) DESC) AS rank
FROM
    orders AS o
    LEFT JOIN products AS p ON o.product_id = p.product_id
WHERE
    o.user_id IS NOT NULL
    AND o.order_date BETWEEN '2023-01-01' AND '2023-12-31'
    AND o.is_canceled = 0
GROUP BY
    o.user_id
ORDER BY
    rank
LIMIT 10

・表10-3:SQL10-2の実行結果

user_id

total_price

rank

A000276

24712867

1

A000922

12462096

2

A000026

8425735

3

A000496

6035498

4

A000799

5676461

5

A000861

4527191

6

A000195

3662527

7

A000028

3008086

8

A000373

2618627

9

A000212

2453066

10

・・・

・・・

・・・

SQL10-1と比較すると、rankというカラムが追加され、購入金額の多い順に1位、2位……と順位がつきました。RANK() OVER (ORDER BY SUM(p.price) DESC)の部分がWINDOW関数の記述です。OVERの中にORDER BY SUM(p.price) DESCと指定することで、購入金額(total_price)が多い順に順位を計算しています。

重要なポイントは、GROUP BY o.user_idで集約しているにもかかわらず、rankカラムには各ユーザーの順位が付いているという点です。このように、WINDOW関数は集計後の各行に対して計算を追加できるため、GROUP BYとの組み合わせで非常に便利に使えます。

・図10-4:RANKのイメージ

RANKのイメージ
RANKのイメージ

RANK()とDENSE_RANK()の違い

RANK関数に似た関数としてDENSE_RANK() があります。2つの違いは、同じ値(同順位)があったときの次の順位の付け方にあります。

例えば、2位が2人いた場合を考えてみましょう。RANK()は、2位が2人いたら次の順位は「4位」になります(3位が飛ばされます)。一方、DENSE_RANK()は、2位が2人いても次の順位は「3位」になります(順位が飛ばされません)。

・図10-5:RANK()とDENSE_RANK()の違い

RANK()とDENSE_RANK()の違い
RANK()とDENSE_RANK()の違い

どちらを使うかは分析の目的によります。「売上上位5社に何社入っているか」のように同率を1つの順位として数えたいときはDENSE_RANK()が便利です。一方、「全体で何番目に位置するか」を正確に示したいときはRANK()が適しています。

グループ内で順位をつける〜PARTITION BY〜

PARTITION BYとは

WINDOW関数のOVERの中で指定できるPARTITION BYは、グループを分けてグループ内で計算を行うための指定です。GROUP BYと似た名前ですが、PARTITION BYはWINDOW関数のための区切りを定義するだけで、行をまとめることはありません。

例えば、「大カテゴリ別に商品の人気ランキングを出したい」という場合を考えてみましょう。カテゴリをまたいで全商品をランキングしてしまうと、注文件数の多い日用品ばかりが上位を占めてしまいます。PARTITION BYを使えば、「日用品の中での1位、2位、3位……」「食品の中での1位、2位、3位……」というように、カテゴリの中での順位を計算できます。

・図10-6:PARTITION BYのイメージ

PARTITION BYのイメージ
PARTITION BYのイメージ

PARTITION BYの使い方

実際にPARTITION BYを使って、大カテゴリ内での商品ランキングを集計したのがSQL10-3になります。まずはSQL10-3を実行して出力結果を確認してみましょう。第9章で学んだWITH句を使って、商品ごとの注文件数を先に集計してから、RANK() OVER (PARTITION BY ...)でランキングをつけています。

★ハンズオン〜SQLを実行してみよう〜

・SQL10-3

</>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
WITH product_counts AS (
    SELECT
        p.large_category,
        p.name,
        COUNT(o.order_id) AS order_count
    FROM
        orders AS o
        LEFT JOIN products AS p ON o.product_id = p.product_id
    WHERE
        o.is_canceled = 0
    GROUP BY
        p.large_category,
        p.name
)

SELECT
    large_category,
    name,
    order_count,
    RANK() OVER (PARTITION BY large_category ORDER BY order_count DESC) AS rank
FROM
    product_counts
ORDER BY
    large_category,
    rank

・表10-7:SQL10-3の実行結果

large_category

name

order_count

rank

インテリア

レザーソファ3人掛け

788

1

インテリア

テレビ台 150cm

733

2

インテリア

デスクチェア

367

3

ファッション

腕時計

2404

1

ファッション

靴下3足セット

1461

2

ファッション

トートバッグ

698

3

日用品

ホワイトプレート22cm

27781

1

日用品

美容液

13623

2

日用品

レターセット

8347

3

電化製品

DVD 10枚セット

5883

1

・・・

・・・

・・・

・・・

出力結果を確認すると、大カテゴリ(large_category)の中で、注文件数(order_count)の多い順に1位、2位……と順位(rank)がついているのが確認できます。インテリアの中での1位は「レザーソファ3人掛け」、ファッションの中での1位は「腕時計」というように、カテゴリをまたいだ順位ではなく、各カテゴリ内での順位になっています。

SQL10-3のRANK() OVER (PARTITION BY large_category ORDER BY order_count DESC)の部分がWINDOW関数の記述です。PARTITION BY large_categoryと指定することで、large_categoryの値ごとにグループを分け、その中でORDER BY order_count DESCに基づく順位を計算しています。

このように、PARTITION BYを使うと、グループの区切りを定義しながら各行に計算結果を付け加えることができます。GROUP BYでは行がまとまってしまうのに対し、PARTITION BYを使ったWINDOW関数では元の行がそのまま残り、カテゴリ内の順位だけが追加されているのが重要なポイントです。

累計を計算する〜SUM() OVER()〜

SUM() OVER()の使い方

WINDOW関数は順位をつける以外にも、累計(累積合計)を計算するときにも便利です。SUM()をWINDOW関数として使う場合は、SUM(カラム名) OVER (ORDER BY カラム名)のように記述します。

例えば、「月ごとの売上金額を確認しながら、1月からの累計売上も同時に見たい」という場合を考えてみましょう。SQL10-4では、WITH句を使って月ごとの売上を先に集計してから、SUM() OVER (ORDER BY month)で累計を計算しています。まずはSQL10-4を実行して出力結果を確認してみましょう。

★ハンズオン〜SQLを実行してみよう〜

・SQL10-4

</>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
WITH monthly_revenue AS (
    SELECT
        TO_CHAR(o.order_date, 'YYYY-MM') AS month,
        SUM(p.price) AS monthly_revenue
    FROM
        orders AS o
        LEFT JOIN products AS p ON o.product_id = p.product_id
    WHERE
        o.order_date BETWEEN '2023-01-01' AND '2023-12-31'
        AND o.is_canceled = 0
    GROUP BY
        TO_CHAR(o.order_date, 'YYYY-MM')
)

SELECT
    month,
    monthly_revenue,
    SUM(monthly_revenue) OVER (ORDER BY month) AS cumulative_revenue
FROM
    monthly_revenue
ORDER BY
    month

・表10-8:SQL10-4の実行結果

month

monthly_revenue

cumulative_revenue

2023-01

15984704

15984704

2023-02

14111551

30096255

2023-03

16807339

46903594

2023-04

15323752

62227346

2023-05

15645469

77872815

2023-06

15385603

93258418

2023-07

16251055

109509473

2023-08

15324931

124834404

2023-09

15128303

139962707

2023-10

15968527

155931234

2023-11

15352728

171283962

2023-12

16922547

188206509

出力結果を確認すると、monthly_revenueが月ごとの売上金額、cumulative_revenueが1月から各月までの累計売上金額になっています。例えば、2023年2月のcumulative_revenueは「15,984,704(1月)+ 14,111,551(2月)= 30,096,255」になっているのが確認できます。

SUM(monthly_revenue) OVER (ORDER BY month)の部分がWINDOW関数の記述です。ORDER BY monthと指定することで、月の昇順(1月、2月、3月……)に沿って、その行までの合計値を計算しています。このようにWINDOW関数のSUM()は、全体の合計ではなく、指定した順序に従って行ごとに累積した合計を返します。

・図10-9:SUM() OVER()のイメージ

SUM() OVER()のイメージ
SUM() OVER()のイメージ

累計を計算するSUM() OVER()は、売上のトレンド把握や予算達成率の確認など、実務のデータ分析でも頻繁に登場します。「月次の売上を見ながら、年間目標に対してどのくらい進んでいるか」を確認したい場面などで大変役立ちます。

その他のWINDOW関数

ROW_NUMBER()

RANK()と似た関数としてROW_NUMBER() があります。ROW_NUMBER()は各行に対してユニークな連番(行番号)を付与します。RANK()と異なる点は、同じ値があっても必ず異なる番号が割り当てられることです。

・RANK()とROW_NUMBER()の比較

購入金額

RANK()

ROW_NUMBER()

24712867

1

1

12462096

2

2

8425735

3

3

8425735

3

4

5676461

5

5

上の例のように、同じ購入金額が2件ある場合、RANK()では両方とも「3位」になりますが、ROW_NUMBER()では「3」と「4」という異なる番号が付与されます。ROW_NUMBER()は「上位N件だけデータを取得したい」という絞り込みに使うことが多いです。

WINDOW関数には他にも、前の行の値を参照できるLAG()や、後の行の値を参照できるLEAD()などがあります。データ分析の現場では、RANK()・DENSE_RANK()・SUM() OVER()の3つが特によく使われるので、まずはこれらを押さえておきましょう。

データ分析におけるWINDOW関数の考え方

WINDOW関数の最大の特徴は、行を集約せずに集計結果を各行に付け加えられる点です。GROUP BYを使うと行数が減るため、元のデータと組み合わせて分析したいときはサブクエリやWITH句が必要でした。WINDOW関数を使えば、そのような複雑な記述をしなくても、1つのSQLで集計値を各行に付け加えることができます。

・図10-10:WINDOW関数の考え方

WINDOW関数の考え方
WINDOW関数の考え方

WINDOW関数は、次のような場面で特に力を発揮します。

  • 順位付け:売上ランキング、ユーザーの貢献度ランキングなど
  • グループ内順位:カテゴリ内での人気商品ランキング、地域別の集計順位など
  • 累計計算:月次売上の累計、目標達成率の推移など

データ分析の現場でも、WINDOW関数を使いこなせると、複雑な集計が一段とシンプルに書けるようになります。GROUP BYや第9章のサブクエリ・WITH句と組み合わせて活用することで、より高度なデータ分析ができるようになります。ぜひ積極的に使ってみてください。

演習問題

演習問題10-1

2023年の年間購入金額が多い順に上位5位までのユーザーIDと購入金額、順位を取得してください。また、ユーザー情報が空のデータとキャンセルされた購入データは除外してください。

演習問題10-2

第9章で学んだWITH句とWINDOW関数を組み合わせて、2023年の月ごとの売上金額と、1月からの累計売上金額を計算してください。ただし、キャンセルされた購入データは除外してください。

前のレッスン

複数のクエリを組み合わせる〜 サブクエリ / WIHT 〜

コース完了

コース一覧に戻る