日付の集計と EXISTS

上級143

日付から年月を取り出して月次集計する方法と、EXISTS で「条件に合う行が存在するか」を判定する書き方を練習します。

日付を 「年月」 単位にまとめる

2026-01-15 のような日付をそのまま GROUP BY すると、 日ごとにバラバラの

グループになってしまいます。 月次で見たいときは 年月の文字列に切り落として

から集計します。

SELECT strftime('%Y-%m', order_date) AS ym, COUNT(*) AS cnt
FROM orders
GROUP BY ym
ORDER BY ym;

strftime は日付を指定した書式の文字列に変える関数です。

書式意味
%Y年 4 桁2026
%m月 2 桁01
%d日 2 桁15
%Y-%m年月2026-01

%Y-%m並べ替えるとそのまま時系列順 になるのが利点です。

1月 2月 … のような文字列にすると、 10 月が 1 月の次に来てしまいます。

> 日付関数の名前は データベースごとに違います。 SQLite は strftime、

> MySQL は DATE_FORMAT、 PostgreSQL は TO_CHAR / DATE_TRUNC です。

> 考え方は同じで、 関数名だけ読み替えれば通用します。

JOIN と組み合わせて月次売上を出す

金額は orders には無く products にあるので、 つないでから集計します。

SELECT strftime('%Y-%m', o.order_date) AS ym,
       SUM(p.price * o.quantity) AS sales
FROM orders o
JOIN products p ON p.id = o.product_id
GROUP BY ym
ORDER BY ym;

EXISTS — 「あるかどうか」 だけを見る

SELECT name FROM products p
WHERE EXISTS (
  SELECT 1 FROM orders o
  JOIN customers c ON c.id = o.customer_id
  WHERE o.product_id = p.id AND c.city = '東京'
);

EXISTS は 中のクエリが 1 行でも返せば真 になります。 中で

SELECT 1 と書いているのは、 値そのものは使わず 「あるかどうか」 しか

見ないからです。 SELECT * でも動きますが、 意図が伝わる 1 が慣用です。

外側の p.id を 内側から参照している点に注目してください。 このように

外の行ごとに内側を評価する サブクエリを 相関サブクエリと呼びます。

EXISTS と IN と JOIN の使い分け

同じことは IN でも JOIN でも書けますが、 向き不向きがあります。

- EXISTS … 「存在するか」 だけ知りたい。 1 件見つかった時点で打ち切れる。

NULL があっても正しく動く

- IN … 内側が返す値の一覧と突き合わせたい。 リストが小さいときは読みやすい。

ただし 内側に NULL が混ざると NOT IN が期待どおりに動かない

- JOIN … 相手側の列も 結果に並べたい とき。 相手が複数行あると

行が増える (重複する) 点に注意。

「条件として使うだけで、 結果には出さない」 なら EXISTS が素直です。

NOT EXISTS — 「1 件も無い」 を探す

SELECT name FROM products p
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.product_id = p.id);

売れていない商品、 一度も来店していない会員、 提出のない課題 —

「無いもの」 を探す のは EXISTS の得意分野です。

(このサンプルデータでは 全商品が注文済みなので 結果は 0 件になります。

0 件が返るのも 正しい答えです。)

よくあるつまずき

- 日付を文字列として比較して失敗するYYYY-MM-DD 形式なら

文字列比較でも時系列順になりますが、 2026/1/5 のような形式だと崩れます。

- GROUP BY に別名が使えない DB がある — SQLite は使えますが、

厳密な DB では GROUP BY strftime('%Y-%m', order_date) と書き直します。

- NOT IN と NULL — 内側に 1 つでも NULL があると NOT IN は

常に偽になります。 迷ったら NOT EXISTS を使うのが安全です。

実務での使いどころ

月次・年次のレポートは ほぼ全部これです。 EXISTS のほうは

「まだ登録していないユーザー」 「在庫がない商品」 のような 抜け漏れの洗い出しで、

データの品質チェックにも そのまま使えます。

データベースを初期化中...
SQL エディタCtrl+Enter で実行
SQL を入力して実行してください
練習問題 — 0/3 完了 (0/100pt)
orders を年月ごとにまとめ、ym (YYYY-MM 形式) と注文件数 cnt を、ym の昇順で取得してください。
30pt
orders と products を結合して、年月ごとの売上金額 (price × quantity の合計) を ym と sales の 2 列で、ym の昇順で取得してください。
35pt
東京に住む顧客が一度でも注文した商品の name を、EXISTS を使って name 順に取得してください。
35pt