SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS prev_month_sales
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
クエリの解説:
strftime('%Y-%m', order_date) → 日付を「年-月」形式に変換
SUM(total_amount) → 月ごとの売上合計
LAG(SUM(total_amount), 1) → 1行前(前月)の売上を取得
ORDER BY strftime('%Y-%m', order_date) → 年月順に並べて「前」を決定
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS prev_month_sales
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ステップ2:増減額を計算
今月売上 – 前月売上 で増減額を求めます。
※横にスクロールできます
SUM(total_amount) – LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS diff_amount
ステップ3:増減率を計算
(今月 – 前月) ÷ 前月 × 100 で増減率を求めます。
※横にスクロールできます
ROUND(
(SUM(total_amount) – LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
)) * 100.0 / LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
),
2
) AS growth_rate
計算式の解説:
* 100.0 → パーセントに変換(.0をつけて小数計算にする)
/ LAG(...) → 前月売上で割る
ROUND(..., 2) → 小数点以下2桁に丸める
完成コード:
※横にスクロールできます
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS prev_month_sales,
SUM(total_amount) – LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS diff_amount,
ROUND(
(SUM(total_amount) – LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
)) * 100.0 / LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
),
2
) AS growth_rate
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
例えば、お正月商戦で1月の売上が高い小売業の場合、「1月 vs 2月」で比較すると必ず下がります。でも「今年1月 vs 去年1月」なら、季節要因を除いた成長が分かります。
3-2. LAGで12ヶ月前を参照する
やりたいこと:月次売上の前年同月比を計算する
月次データの場合、12行前 = 12ヶ月前 = 前年同月になります。
※横にスクロールできます
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
LAG(SUM(total_amount), 12) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS prev_year_sales,
ROUND(
(SUM(total_amount) – LAG(SUM(total_amount), 12) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
)) * 100.0 / LAG(SUM(total_amount), 12) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
),
2
) AS yoy_growth
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
クエリの解説:
LAG(SUM(total_amount), 12) → 12行前(12ヶ月前)の売上を取得
yoy_growth → Year over Year(前年比)の略でよく使われる名前
結果イメージ(2024年のデータ):
year_month
monthly_sales
prev_year_sales
yoy_growth
2024-01
1,000,000
800,000(2023年1月)
25.00
2024-02
1,200,000
1,000,000(2023年2月)
20.00
2024-03
1,500,000
1,200,000(2023年3月)
25.00
最初の12ヶ月間は前年データがないのでNULLになります。
3-3. LAGの第2引数のまとめ
📌 LAGの第2引数(何行前か)
データの単位
LAGの指定
意味
月次データ
LAG(列, 1)
前月
月次データ
LAG(列, 12)
前年同月
週次データ
LAG(列, 1)
先週
週次データ
LAG(列, 52)
前年同週
日次データ
LAG(列, 1)
前日
日次データ
LAG(列, 7)
先週の同じ曜日
日次データ
LAG(列, 365)
前年同日
🔄 4. PARTITION BYとの組み合わせ
4-1. グループごとに前の行を参照する
やりたいこと:カテゴリごとに、前月の売上を取得する
PARTITION BYと組み合わせることで、グループごとに「前の行」を参照できます。
※横にスクロールできます
SELECT
category,
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
LAG(SUM(total_amount), 1) OVER (
PARTITION BY category
ORDER BY strftime(‘%Y-%m’, order_date)
) AS prev_month_sales
FROM orders
INNER JOIN products ON orders.product_id = products.product_id
GROUP BY category, strftime(‘%Y-%m’, order_date)
ORDER BY category, year_month;
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
LEAD(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS next_month_sales
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
結果イメージ:
year_month
monthly_sales
next_month_sales
2024-01
1,000,000
1,200,000
2024-02
1,200,000
1,500,000
2024-03
1,500,000
1,800,000
2024-04
1,800,000
NULL(翌月なし)
最後の行(4月)は翌月データがないのでNULLになります。
5-4. 例2:前月・今月・翌月を同時に表示
やりたいこと:LAGとLEADを同時に使って、前月・今月・翌月を並べる
※横にスクロールできます
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS prev_month,
SUM(total_amount) AS current_month,
LEAD(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS next_month
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
クエリの解説:
LAG(..., 1) → 1行前(前月)
SUM(...) → 今月(現在の行)
LEAD(..., 1) → 1行後(翌月)
ポイント:
LAGとLEADは同時に使えます。これで前後を一度に比較できます。
結果イメージ:
year_month
prev_month
current_month
next_month
2024-01
NULL
1,000,000
1,200,000
2024-02
1,000,000
1,200,000
1,500,000
2024-03
1,200,000
1,500,000
1,800,000
2024-04
1,500,000
1,800,000
NULL
5-5. 例3:次回購入までの日数を計算
やりたいこと:顧客の注文履歴から、次の購入日と次回までの日数を計算する
※横にスクロールできます
SELECT
c.customer_name,
o.order_date,
o.total_amount,
LEAD(o.order_date, 1) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
) AS next_order_date,
julianday(LEAD(o.order_date, 1) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
)) – julianday(o.order_date) AS days_to_next
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
ORDER BY c.customer_name, o.order_date;
FIRST_VALUE(取得する列) OVER (
ORDER BY 並び順の列
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)
ポイント:ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW を指定すると、「最初から現在まで」の範囲で最初の値を取得します。
6-3. 例1:カテゴリ内の最安値を表示
やりたいこと:各商品に、そのカテゴリ内の最安値を表示する
※横にスクロールできます
SELECT
product_name,
category,
price,
FIRST_VALUE(price) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS min_price_in_category
FROM products
ORDER BY category, price;
クエリの解説:
PARTITION BY category → カテゴリごとに区切る
ORDER BY price → 価格の安い順に並べる
FIRST_VALUE(price) → 最初(最も安い)の価格を取得
なぜ最安値になるか:
ORDER BY priceで価格の安い順に並べているので、「最初の値」= 「最も安い価格」になります。
結果イメージ:
product_name
category
price
min_price_in_category
バナナ
果物
120
120
りんご
果物
150
120
みかん
果物
180
120
ぶどう
果物
500
120
果物カテゴリの全商品に、最安値の120円が表示されます。
6-4. 例2:最安値との差額を計算
やりたいこと:各商品の価格と、カテゴリ最安値との差額を計算する
※横にスクロールできます
SELECT
product_name,
category,
price,
FIRST_VALUE(price) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS min_price,
price – FIRST_VALUE(price) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS diff_from_min
FROM products
ORDER BY category, price;
結果イメージ:
product_name
category
price
min_price
diff_from_min
バナナ
果物
120
120
0
りんご
果物
150
120
30
ぶどう
果物
500
120
380
6-5. 例3:年初からの成長率を計算
やりたいこと:月次売上と年初の売上を比較し、年初からの成長率を計算する
コードを段階的に見ていきましょう。
ステップ1:年ごとに区切って年初売上を取得
※横にスクロールできます
FIRST_VALUE(SUM(total_amount)) OVER (
PARTITION BY strftime(‘%Y’, order_date)
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS jan_sales
ポイント:
PARTITION BY strftime('%Y', order_date) → 年ごとに区切る
2024年1月〜12月は2024年グループ、2025年1月〜12月は2025年グループ
各グループの「最初の値」= 年初(1月)の売上
ステップ2:年初比(成長率)を計算
※横にスクロールできます
ROUND(
(SUM(total_amount) – FIRST_VALUE(SUM(total_amount)) OVER (…)) * 100.0
/ FIRST_VALUE(SUM(total_amount)) OVER (…),
2
) AS ytd_growth
完成コード:
※横にスクロールできます
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
FIRST_VALUE(SUM(total_amount)) OVER (
PARTITION BY strftime(‘%Y’, order_date)
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS jan_sales,
ROUND(
(SUM(total_amount) – FIRST_VALUE(SUM(total_amount)) OVER (
PARTITION BY strftime(‘%Y’, order_date)
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)) * 100.0 / FIRST_VALUE(SUM(total_amount)) OVER (
PARTITION BY strftime(‘%Y’, order_date)
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
),
2
) AS ytd_growth
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
結果イメージ:
year_month
monthly_sales
jan_sales
ytd_growth
2024-01
1,000,000
1,000,000
0.00
2024-02
1,200,000
1,000,000
20.00
2024-03
1,500,000
1,000,000
50.00
2024-06
2,000,000
1,000,000
100.00
6月は年初(1月)の2倍になっているので、年初比+100%です。
6-6. 例4:顧客の初回購入額との比較
やりたいこと:顧客の各注文に、初回購入額と初回からの変化を表示する
※横にスクロールできます
SELECT
c.customer_name,
o.order_date,
o.total_amount,
FIRST_VALUE(o.total_amount) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS first_purchase,
o.total_amount – FIRST_VALUE(o.total_amount) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS diff_from_first
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
ORDER BY c.customer_name, o.order_date;
必ずROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING を指定して、「最初から最後まで」を対象にしましょう。
7-2. フレーム指定の違いを理解する
📌 LAST_VALUEのフレーム指定
フレーム指定
意味
結果
指定なし(デフォルト)
最初から現在まで
❌ 現在の行が「最後」になる
UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
最初から最後まで
✅ 本当の「最後の値」が取れる
7-3. LAST_VALUE関数の正しい構文
📌 LAST_VALUE関数の書き方(正しい方法)
※横にスクロールできます
LAST_VALUE(取得する列) OVER (
ORDER BY 並び順の列
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
)
UNBOUNDED FOLLOWING(最後まで)を必ず指定しましょう!
7-4. 例1:カテゴリ内の最高値を表示
やりたいこと:各商品に、そのカテゴリ内の最高値を表示する
※横にスクロールできます
SELECT
product_name,
category,
price,
LAST_VALUE(price) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS max_price_in_category
FROM products
ORDER BY category, price;
クエリの解説:
ORDER BY price → 価格の安い順に並べる
LAST_VALUE(price) → 最後(最も高い)の価格を取得
UNBOUNDED FOLLOWING → 最後の行まで見る(これが重要!)
結果イメージ:
product_name
category
price
max_price_in_category
バナナ
果物
120
500
りんご
果物
150
500
みかん
果物
180
500
ぶどう
果物
500
500
果物カテゴリの全商品に、最高値の500円が表示されます。
7-5. 例2:最高値との差額を計算
やりたいこと:各商品の価格と、カテゴリ最高値との差額を計算する
※横にスクロールできます
SELECT
product_name,
category,
price,
LAST_VALUE(price) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS max_price,
LAST_VALUE(price) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) – price AS diff_from_max
FROM products
ORDER BY category, price;
結果イメージ:
product_name
category
price
max_price
diff_from_max
バナナ
果物
120
500
380
りんご
果物
150
500
350
ぶどう
果物
500
500
0
7-6. 例3:顧客の最終購入額を表示
やりたいこと:顧客の各注文に、最終購入額を表示する
※横にスクロールできます
SELECT
c.customer_name,
o.order_date,
o.total_amount,
LAST_VALUE(o.total_amount) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS last_purchase
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
ORDER BY c.customer_name, o.order_date;
活用シーン:
顧客の現状把握:最終購入額を全履歴に表示
離脱分析:最終購入額が低い顧客の特徴を分析
年末目標との比較:年末の売上を全月に表示して進捗を確認
📊 8. 4つの関数の組み合わせ
8-1. 前後と最初・最後を同時に取得
やりたいこと:月次売上に、前月・翌月・初月・最終月を全て表示する
※横にスクロールできます
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
— LAG: 前月
LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS prev_month,
— LEAD: 翌月
LEAD(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS next_month,
— FIRST_VALUE: 初月
FIRST_VALUE(SUM(total_amount)) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS first_month,
— LAST_VALUE: 最終月
LAST_VALUE(SUM(total_amount)) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS last_month
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
クエリの解説:
LAG(..., 1) → 1行前(前月)
LEAD(..., 1) → 1行後(翌月)
FIRST_VALUE → 最初の行(初月)
LAST_VALUE → 最後の行(最終月)
ポイント:
4つの関数を同時に使うことで、様々な角度から比較できます。
8-2. 商品のカテゴリ内位置づけ分析
やりたいこと:各商品に、カテゴリ内の最安商品名・価格、最高商品名・価格を表示する
※横にスクロールできます
SELECT
product_name,
category,
price,
— 最安商品名
FIRST_VALUE(product_name) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS cheapest_product,
— 最安値
FIRST_VALUE(price) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS min_price,
— 最高商品名
LAST_VALUE(product_name) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS most_expensive_product,
— 最高値
LAST_VALUE(price) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS max_price
FROM products
ORDER BY category, price;
結果イメージ:
product_name
category
price
cheapest
most_expensive
バナナ
果物
120
バナナ(120円)
ぶどう(500円)
りんご
果物
150
バナナ(120円)
ぶどう(500円)
ぶどう
果物
500
バナナ(120円)
ぶどう(500円)
💼 9. 実務での活用例
9-1. 売上トレンド分析(前月比・前年同月比・年初比)
やりたいこと:月次売上に、3つの成長率指標を同時に表示する
※横にスクロールできます
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
— 前月比
ROUND(
(SUM(total_amount) – LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
)) * 100.0 / LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
),
2
) AS mom_growth,
— 前年同月比
ROUND(
(SUM(total_amount) – LAG(SUM(total_amount), 12) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
)) * 100.0 / LAG(SUM(total_amount), 12) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
),
2
) AS yoy_growth,
— 年初比
ROUND(
(SUM(total_amount) – FIRST_VALUE(SUM(total_amount)) OVER (
PARTITION BY strftime(‘%Y’, order_date)
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)) * 100.0 / FIRST_VALUE(SUM(total_amount)) OVER (
PARTITION BY strftime(‘%Y’, order_date)
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
),
2
) AS ytd_growth
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
3つの成長率指標:
mom_growth(Month over Month)→ 前月比
yoy_growth(Year over Year)→ 前年同月比
ytd_growth(Year to Date)→ 年初比
9-2. 顧客の購買行動分析
やりたいこと:顧客ごとに、初回購入額・前回からの変化・購入間隔を分析する
※横にスクロールできます
SELECT
c.customer_name,
o.order_date,
o.total_amount,
— 初回購入額
FIRST_VALUE(o.total_amount) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS first_purchase,
— 初回からの変化
o.total_amount – FIRST_VALUE(o.total_amount) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS diff_from_first,
— 前回購入日
LAG(o.order_date, 1) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
) AS prev_order_date,
— 前回からの日数
julianday(o.order_date) – julianday(LAG(o.order_date, 1) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
)) AS days_since_prev
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
ORDER BY c.customer_name, o.order_date;
活用シーン:
顧客育成:初回購入から購入額が成長しているか確認
離脱予防:購入間隔が長くなっている顧客をアラート
優良顧客発見:初回から継続的に購入額が増えている顧客
9-3. 在庫変動の監視
やりたいこと:在庫の増減を追跡し、入庫・出庫を判定する
※横にスクロールできます
SELECT
product_name,
check_date,
stock,
LAG(stock, 1) OVER (
PARTITION BY product_name
ORDER BY check_date
) AS prev_stock,
stock – LAG(stock, 1) OVER (
PARTITION BY product_name
ORDER BY check_date
) AS stock_change,
CASE
WHEN stock – LAG(stock, 1) OVER (
PARTITION BY product_name
ORDER BY check_date
) > 0 THEN ‘入庫’
WHEN stock – LAG(stock, 1) OVER (
PARTITION BY product_name
ORDER BY check_date
) < 0 THEN '出庫'
ELSE '変化なし'
END AS change_type
FROM stock_history
ORDER BY product_name, check_date;
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS prev_month_sales
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
解説:
LAG(列, 1) で1行前(前月)のデータを取得
ORDER BY で年月順に並べることで「前の行」= 「前月」になる
問題 2基本
前月比の計算
月次売上の前月比(増減率)を計算してください。
※横にスクロールできます
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS prev_month_sales,
ROUND(
(SUM(total_amount) – LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
)) * 100.0 / LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
),
2
) AS mom_growth
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
解説:
計算式: (今月 – 前月) ÷ 前月 × 100
* 100.0 で小数計算にしてパーセントに変換
問題 3基本
翌月売上の表示
月ごとの売上と翌月売上を表示してください。
※横にスクロールできます
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
LEAD(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS next_month_sales
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
解説:
LEAD(列, 1) で1行後(翌月)のデータを取得
LAGの逆方向(未来)を参照
問題 4応用
前年同月比
月次売上の前年同月比を計算してください。
※横にスクロールできます
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
LAG(SUM(total_amount), 12) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS prev_year_sales,
ROUND(
(SUM(total_amount) – LAG(SUM(total_amount), 12) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
)) * 100.0 / LAG(SUM(total_amount), 12) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
),
2
) AS yoy_growth
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
解説:
LAG(列, 12) で12行前(12ヶ月前 = 前年同月)を取得
季節変動の影響を除いた成長率を計算できる
問題 5応用
前月・翌月の同時表示
月次売上に、前月売上と翌月売上を同時に表示してください。
※横にスクロールできます
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS prev_month,
SUM(total_amount) AS current_month,
LEAD(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS next_month
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
解説:
LAGとLEADは同時に使える
前後を一度に比較できて便利
問題 6応用
カテゴリ最安値との比較
各商品の価格と、そのカテゴリの最安値を表示してください。(FIRST_VALUEを使用)
※横にスクロールできます
SELECT
product_name,
category,
price,
FIRST_VALUE(price) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS min_price_in_category
FROM products
ORDER BY category, price;
解説:
ORDER BY priceで価格順に並べる
FIRST_VALUEで最初(最も安い)の価格を取得
問題 7応用
カテゴリ最高値との比較
各商品の価格と、そのカテゴリの最高値を表示してください。(LAST_VALUEを使用)
※横にスクロールできます
SELECT
product_name,
category,
price,
LAST_VALUE(price) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS max_price_in_category
FROM products
ORDER BY category, price;
解説:
LAST_VALUEは UNBOUNDED FOLLOWING が必須!
これがないと「現在の行」が「最後」になってしまう
問題 8チャレンジ
顧客の購入間隔
顧客ごとの注文履歴に、前回購入日と前回からの日数を表示してください。
※横にスクロールできます
SELECT
c.customer_name,
o.order_date,
LAG(o.order_date, 1) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
) AS prev_order_date,
julianday(o.order_date) – julianday(LAG(o.order_date, 1) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
)) AS days_since_prev
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
ORDER BY c.customer_name, o.order_date
LIMIT 20;
解説:
PARTITION BY customer_idで顧客ごとに区切る
julianday()で日付を日数に変換して引き算
問題 9チャレンジ
初回購入額との比較
顧客ごとの注文履歴に、初回購入額と初回からの変化額を表示してください。
※横にスクロールできます
SELECT
c.customer_name,
o.order_date,
o.total_amount,
FIRST_VALUE(o.total_amount) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS first_purchase,
o.total_amount – FIRST_VALUE(o.total_amount) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS diff_from_first
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
ORDER BY c.customer_name, o.order_date
LIMIT 20;
解説:
FIRST_VALUEで各顧客の初回購入額を全行に表示
今回 – 初回 で変化額を計算
問題 10チャレンジ
年初比の計算
月次売上と年初の売上を比較し、年初からの成長率を計算してください。
※横にスクロールできます
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
FIRST_VALUE(SUM(total_amount)) OVER (
PARTITION BY strftime(‘%Y’, order_date)
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS jan_sales,
ROUND(
(SUM(total_amount) – FIRST_VALUE(SUM(total_amount)) OVER (
PARTITION BY strftime(‘%Y’, order_date)
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)) * 100.0 / FIRST_VALUE(SUM(total_amount)) OVER (
PARTITION BY strftime(‘%Y’, order_date)
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
),
2
) AS ytd_growth
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
解説:
PARTITION BY 年で年ごとに区切る
FIRST_VALUEで各年の1月(年初)を取得
問題 11チャレンジ
前後と最初最後の同時表示
月次売上に、前月、翌月、初月、最終月の全てを表示してください。
※横にスクロールできます
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS prev_month,
LEAD(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
) AS next_month,
FIRST_VALUE(SUM(total_amount)) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS first_month,
LAST_VALUE(SUM(total_amount)) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS last_month
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
解説:
4つの関数を全て使った総合的なクエリ
LAST_VALUEには UNBOUNDED FOLLOWING を忘れずに!
問題 12チャレンジ
次回購入までの日数
顧客の注文履歴から、次回購入日と次回までの予想日数を計算してください。
※横にスクロールできます
SELECT
c.customer_name,
o.order_date,
LEAD(o.order_date, 1) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
) AS next_order_date,
julianday(LEAD(o.order_date, 1) OVER (
PARTITION BY c.customer_id
ORDER BY o.order_date
)) – julianday(o.order_date) AS days_to_next
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
ORDER BY c.customer_name, o.order_date
LIMIT 20;
解説:
LEADで次の購入日を取得
次 – 今 で次回までの日数を計算
問題 13チャレンジ
カテゴリ内の最安・最高
各商品に、カテゴリ内の最安商品名・最安値、最高商品名・最高値を表示してください。
※横にスクロールできます
SELECT
product_name,
category,
price,
FIRST_VALUE(product_name) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS cheapest_product,
FIRST_VALUE(price) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS min_price,
LAST_VALUE(product_name) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS expensive_product,
LAST_VALUE(price) OVER (
PARTITION BY category
ORDER BY price
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS max_price
FROM products
ORDER BY category, price;
解説:
FIRST_VALUEとLAST_VALUEで商品名と価格の両方を取得
商品の位置づけを詳細に分析できる
問題 14チャレンジ
在庫変動の追跡
在庫履歴から、前日在庫、在庫変動、変動タイプ(入庫/出庫/変化なし)を計算してください。
※横にスクロールできます
SELECT
product_name,
check_date,
stock,
LAG(stock, 1) OVER (
PARTITION BY product_name
ORDER BY check_date
) AS prev_stock,
stock – LAG(stock, 1) OVER (
PARTITION BY product_name
ORDER BY check_date
) AS stock_change,
CASE
WHEN stock – LAG(stock, 1) OVER (
PARTITION BY product_name
ORDER BY check_date
) > 0 THEN ‘入庫’
WHEN stock – LAG(stock, 1) OVER (
PARTITION BY product_name
ORDER BY check_date
) < 0 THEN '出庫'
ELSE '変化なし'
END AS change_type
FROM stock_history
ORDER BY product_name, check_date;
解説:
LAGとCASE式を組み合わせて変動タイプを判定
実務で非常によく使うパターン
問題 15チャレンジ
総合売上分析
月次売上に、前月比、前年同月比、年初比を全て表示してください。
※横にスクロールできます
SELECT
strftime(‘%Y-%m’, order_date) AS year_month,
SUM(total_amount) AS monthly_sales,
— 前月比
ROUND(
(SUM(total_amount) – LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
)) * 100.0 / LAG(SUM(total_amount), 1) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
),
2
) AS mom_growth,
— 前年同月比
ROUND(
(SUM(total_amount) – LAG(SUM(total_amount), 12) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
)) * 100.0 / LAG(SUM(total_amount), 12) OVER (
ORDER BY strftime(‘%Y-%m’, order_date)
),
2
) AS yoy_growth,
— 年初比
ROUND(
(SUM(total_amount) – FIRST_VALUE(SUM(total_amount)) OVER (
PARTITION BY strftime(‘%Y’, order_date)
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)) * 100.0 / FIRST_VALUE(SUM(total_amount)) OVER (
PARTITION BY strftime(‘%Y’, order_date)
ORDER BY strftime(‘%Y-%m’, order_date)
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
),
2
) AS ytd_growth
FROM orders
GROUP BY strftime(‘%Y-%m’, order_date)
ORDER BY year_month;
解説:
実務の売上分析で必須の3つの比較を全て表示
LAGとFIRST_VALUEを組み合わせた総合クエリ
📝 Step 9 のまとめ
✅ 学んだこと
LAG:前の行を参照(前月比、成長率など)
LEAD:次の行を参照(翌月予測、購入間隔など)
FIRST_VALUE:最初の値を取得(初回購入額、年初など)
LAST_VALUE:最後の値を取得(最終購入額、年末など)
前月比・前年同月比:成長率の計算
購入間隔:行動分析への活用
4つの関数の組み合わせ:総合的な分析
📌 4つの関数まとめ
※横にスクロールできます
— LAG: N行前を取得
LAG(列名, N) OVER (ORDER BY 列名)
— LEAD: N行後を取得
LEAD(列名, N) OVER (ORDER BY 列名)
— FIRST_VALUE: 最初の値を取得
FIRST_VALUE(列名) OVER (
ORDER BY 列名
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)
— LAST_VALUE: 最後の値を取得(UNBOUNDED FOLLOWINGが必須!)
LAST_VALUE(列名) OVER (
ORDER BY 列名
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
)