移動平均 — 日々のブレをならす

上級153

日次の売上を 7 日移動平均でならし、累計も並べます。WITH で日次に畳んでからウィンドウ関数をかける手順と、フレーム指定の意味を練習します。

日次のままでは読めない

まず日別の売上を出してみます。

SELECT sale_date, SUM(amount) AS sales
FROM daily_sales
GROUP BY sale_date
ORDER BY sale_date;

90 行返りますが、 11 万の日もあれば 40 万の日もあり、 上がっているのか

下がっているのかが分かりません。 曜日の影響 (週末が高い) が乗っているためです。

7 日移動平均 — 直近 7 日を平均する

その日を含む直近 7 日の平均に置き換えると、 曜日の波が消えて傾向だけが残ります。

7 日にするのは、 1 週間ぶんを必ず 1 回ずつ含むので曜日の偏りが打ち消えるからです。

WITH daily AS (
  SELECT sale_date, SUM(amount) AS sales
  FROM daily_sales
  GROUP BY sale_date
)
SELECT sale_date, sales,
       ROUND(AVG(sales) OVER (
         ORDER BY sale_date
         ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
       ), 1) AS ma7
FROM daily
ORDER BY sale_date;

まず WITH で日次に畳んでから、 その結果にウィンドウ関数をかけるのが型です。

daily_sales に直接 AVG(...) OVER を書くと、 1 日に複数行あるので

「直近 7 」 が 「直近 7 」 になりません。

ROWS BETWEEN の読み方

ROWS BETWEEN 6 PRECEDING AND CURRENT ROW

これは 「6 行前から今の行まで」 = 自分を入れて 7 行という範囲の指定です。

この範囲を フレーム と呼びます。

書き方範囲
INLINE2直近 7 行 (移動平均)
INLINE3先頭から今まで (累計)
INLINE4前後 3 行ずつ (中心化移動平均)

最初の 6 行は 7 日ぶん揃いません。 SQLite は足りないぶんを無視して

あるだけで平均するので、 1 日目の ma7 は その日の値そのものになります。

グラフにするときは最初の数日を捨てるか、 揃っていないことを明示します。

累計も同じ形で出せる

フレームを UNBOUNDED PRECEDING に変えるだけです。

SUM(sales) OVER (ORDER BY sale_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cum

最終日の累計は、 期間全体の合計 (23,029,800) と一致します。

一致するかどうかは、 集計が正しいかを確かめる手軽な検算になります

WHERE はウィンドウ関数より先に評価される

ここは事故が起きやすいところです。

-- ✕ 3 月だけの移動平均になってしまう
WITH daily AS (...)
SELECT sale_date, AVG(sales) OVER (...) FROM daily
WHERE sale_date >= '2026-03-01';

WHERE で先に行を捨ててからウィンドウを計算するので、 2 月末の 6 日ぶんが

平均に入りません。 全期間で計算してから絞りたいなら、 もう一段包みます。

WITH daily AS (...),
     windowed AS (SELECT sale_date, sales, AVG(sales) OVER (...) AS ma7 FROM daily)
SELECT * FROM windowed WHERE sale_date >= '2026-03-01';

よくあるつまずき

- 移動平均が階段状になるORDER BY を書き忘れると範囲がパーティション全体になり、

全部同じ値 (全期間の平均) が並びます。

- 日付が飛んでいる日がある — 売上のない日は行そのものが存在しないため、

「7 行前」 が 「7 日前」 とずれます。 厳密にやるなら日付の一覧を作って

LEFT JOIN しますが、 このデータは 90 日すべてに売上があるので気にせず進めて大丈夫です。

- ROUND を掛ける位置を間違えるROUND(AVG(...) OVER (...), 1) です。

OVER 句の内側に ROUND は書けません。

実務での使いどころ

日次のダッシュボードは、 生の値と移動平均を重ねて描くのが定番です。

生の値で 「今日どうだったか」、 移動平均で 「流れが変わったか」 を同時に見ます。

次のレッスン: 前月比と店舗比較 — 縦の並びを横に変える

データベースを初期化中...
SQL エディタCtrl+Enter で実行
SQL を入力して実行してください
練習問題 — 0/3 完了 (0/85pt)
日別の売上合計を出してください。列は sale_date と sales、日付の昇順で並べます。
20pt
日別売上に 7 日移動平均 ma7 を足してください。列は sale_date・sales・ma7、日付の昇順です。ma7 は ROUND で小数第 1 位まで丸めます。
35pt
日別売上に累計 cum を足してください。列は sale_date・sales・cum、日付の昇順です。最終日の累計が期間全体の合計と一致します。
30pt