日次の売上テーブルから月ごとの売上を出します。strftime で年月を取り出し、GROUP BY でまとめる、レポートの一番の基本を練習します。
このコースでは daily_sales テーブルを使います。 これまでの orders (12 行) と違い、
2026-01-01 から 2026-03-31 までの 90 日 ・ 2 店舗 ・ 563 行 が入っています。
| 列 | 中身 |
|---|---|
| INLINE2 | 売れた日 (2026-01-15 の形の文字列) |
| INLINE4 | 東京本店 / 大阪支店 |
| INLINE5 | INLINE6 の id |
| INLINE7 | 売れた数 |
| INLINE8 | 売上金額 (単価 × 数量) |
まず 1 行見てみましょう。
SELECT * FROM daily_sales LIMIT 5;
日次のままでは行が多すぎて傾向が読めません。 月でまとめるのが第一歩です。
SQLite では strftime で日付の一部を取り出します。
SELECT sale_date, strftime('%Y-%m', sale_date) AS month FROM daily_sales LIMIT 3;
- %Y … 年 (2026)
- %m … 月 (01〜12。 1 桁の月も 0 埋めされる)
- %d … 日
- %w … 曜日 (0=日曜)
'%Y-%m' とつなげて書けば 2026-01 という文字列が返ります。
取り出した年月で GROUP BY します。
SELECT strftime('%Y-%m', sale_date) AS month,
SUM(amount) AS sales
FROM daily_sales
GROUP BY month
ORDER BY month;
これで 3 行 (1月・2月・3月) に畳まれます。 GROUP BY に別名 (month) をそのまま
書けるのは SQLite の親切な仕様で、 SELECT で付けた別名を GROUP BY / ORDER BY から
参照できます。 他のデータベースでは GROUP BY strftime('%Y-%m', sale_date) と
式をもう一度書く必要があることも多いので、 覚えておくと移植のときに困りません。
合計だけ見ていると 「売れた金額は増えたが、 取引の数は減っている」 のような
中身の変化を見落とします。 合計 ・ 件数 ・ 平均は 3 点セットで出します。
SELECT strftime('%Y-%m', sale_date) AS month,
SUM(amount) AS sales,
COUNT(*) AS lines,
ROUND(AVG(amount)) AS avg_line
FROM daily_sales
GROUP BY month
ORDER BY month;
COUNT(*) は明細の行数です。 「何日分あるか」 を数えたいなら
COUNT(DISTINCT sale_date) と書きます。 数えたい単位が行なのか日なのかを
必ず言葉にしてから書くと間違えません。
2026-01 2026-02 2026-03 は文字列として並べても時系列と一致します。
年 4 桁 ・ 月 2 桁 のゼロ埋めが効いているためです。
逆に 2026-1 のように 0 埋めしない形にすると 2026-10 が 2026-2 より
前に来てしまいます。 日付を文字列で扱うときは桁を揃えるのが鉄則です。
- GROUP BY を書き忘れて 1 行だけ返る — 集計関数だけを書くと全体の合計になります。
「月ごと」 と言った時点で GROUP BY が要ります。
- WHERE で月を絞ろうとして効かない — WHERE month = '2026-01' は書けません。
WHERE は GROUP BY より先に評価されるので、 別名はまだ存在しません。
WHERE sale_date >= '2026-01-01' AND sale_date < '2026-02-01' のように
元の列で絞るか、 集計後に絞るなら HAVING を使います。
- AVG(amount) が思ったより小さい — 明細 1 行あたりの平均です。
1 日あたりの平均が欲しいなら SUM(amount) / COUNT(DISTINCT sale_date) です。
月次レポートは 「まず出す」 の定番です。 ここで出した 3 列 (合計 ・ 件数 ・ 平均) が
そのまま 「売れたのは単価が上がったからか、 数が出たからか」 の答えになります。
次のレッスン: 曜日別集計 — 合計ではなく平均で比べる