集計クエリ演習&Day1まとめ
概要
- 日程: Day 1 / セッション 11
- 時間: 16:20-17:00
- 形式: 実習+振り返り
- ゴール: 「部署別の平均給与」「カテゴリ別の商品数」「月別の注文件数」のような集計クエリを3本書き、Day1の学習内容を1分で振り返れる
- 学習形式: ハンズオン実習+振り返り
導入(3分)
Day1の最終セッションです。セッション10で学んだ集約関数と GROUP BY / HAVING を、いよいよ実データで動かします。
今回作るのは3本だけですが、それぞれ「実務で本当に頼まれるレベル」の集計です。書き終わったあと、最後の10分でDay1全体を振り返り、Day2への接続をします。残りの体力を3本のクエリと振り返りに使い切りましょう。
本編(5分)
集計クエリの組み立て手順
集計クエリは、日本語で組み立てを言語化してから書くと、迷子になりません。
- 何を数えたい/平均したい? → 集約関数を決める(COUNT/AVG/SUM等)
- 何ごとに集計したい? →
GROUP BYの列を決める - 元データを絞る必要は? →
WHEREを書く - 集計後の結果を絞る必要は? →
HAVINGを書く - 並べたい? →
ORDER BYを書く
たとえると、料理の段取りメモです。「材料を切る → 炒める → 味付け → 盛り付け」の順番を頭に持っておくと、レシピを見なくても作れるようになります。SQLも同じ。
ここがポイント
- 「何ごとに」を見つけたら、それが
GROUP BYの列 - SELECT に書く列は「GROUP BYの列」または「集約関数の中の列」だけ
- 並びは
WHERE → GROUP BY → HAVING → ORDER BY
実習・演習(22分)
課題1:部署別の平均給与
employees テーブルから「部署IDごとの社員数と平均給与」を、平均給与の降順で取得してください。
ヒント:
- 集約関数:
COUNT(*)とAVG(salary) - グループ化:
department_id - 並び:平均給与の降順
期待する結果のイメージ(数値は例):
| department_id | 社員数 | 平均給与 |
|---|---|---|
| 4 | 1 | 550000 |
| 2 | 3 | ... |
書けたら、ROUND(AVG(salary), 0) で小数点を丸める版にも挑戦してみてください。
課題2:カテゴリ別の商品数と平均価格
products テーブルから「カテゴリIDごとの商品数・平均価格・最高価格」を取得し、商品数の多い順に並べてください。
ヒント:
- 集約:
COUNT(*)AVG(price)MAX(price) - グループ化:
category_id - 並び:商品数の降順
書けたら、HAVING COUNT(*) >= 3 を追加して「商品が3件以上あるカテゴリだけ」に絞る版も書いてみましょう。WHERE と HAVING がどう違うか、自分の手で確認するのが目的です。
課題3:2024年の月別注文件数
orders テーブルから「2024年における月ごとの注文件数と売上合計」を、月の昇順で取得してください。
ヒント:
- 「月」を取り出す関数:
DATE_TRUNC('month', order_date)またはEXTRACT(MONTH FROM order_date)を使う - 集約:
COUNT(*)SUM(total_amount) - フィルター:
WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01' - グループ化:月(上で取り出した値)
- 並び:月の昇順
参考サンプル:
SELECT DATE_TRUNC('month', order_date) AS 月,
COUNT(*) AS 注文件数,
SUM(total_amount) AS 売上合計
FROM orders
WHERE order_date >= '2024-01-01'
AND order_date < '2025-01-01'
GROUP BY DATE_TRUNC('month', order_date)
ORDER BY 月 ASC;
このクエリは少し難しいので、まずそのままコピペで動かしてみて、自分の言葉で「ここでは何をしているか」をAIに説明してみるのもおすすめです。
成果物
- 3本のSQLクエリ(
.sqlファイル) - 各クエリの結果サマリ(件数・主な数値)
- 「自分が一番つまずいたところ」と「AIにどう聞いて解決したか」のメモ
ヒント
GROUP BYの列を変えると見え方が変わります。たとえば課題3で「月」ではなく「statusごと」にしてみると、注文ステータスの内訳が見えてきます- 結果が0件でも文法は合っている可能性大。
WHEREを緩めて確認しましょう - 詰まったら AI に「このSQLを書いたが意図と違う。期待は○○、結果は△△」と仮説付きで相談を
Day1まとめ(10分)
一言でまとめると
Day1のテーマは「データを読む」。皆さんは今日、次のことを身につけました。
(S01,S03)"] --> B["環境構築
(S02,S04)"] B --> C["データ型
(S05)"] C --> D["SELECT基本
(S06)"] D --> E["WHERE条件
(S07)"] E --> F["並び替え・パターン
(S08,S09)"] F --> G["集約・GROUP BY
(S10,S11)"]
ぱっと振り返ると:
- テーブル=表、レコード=行、カラム=列、主キー=行を一意に識別、外部キー=他テーブルとの繋がり
- psql で
\dt\d\l\qを覚えた - 文字列は
'、数値はクォートなし WHEREORDER BYLIKEINBETWEENを組み合わせて1テーブルを自在に絞り込めるCOUNT/SUM/AVG/MAX/MIN+GROUP BY+HAVINGで集計ができる
これだけで、現実のビジネスで頼まれる「データ抽出依頼」の半分は答えられます。
明日(Day2)の予告
Day2は「データを 変える ・ つなぐ ・ 設計する」がテーマです。
- 変える:INSERT / UPDATE / DELETE
- つなぐ:JOIN(複数テーブルを跨いだ抽出)
- 設計する:CREATE TABLE、制約、インデックス、トランザクション
今日は「他人が用意したテーブル」を読むだけでしたが、明日は「自分でテーブルを作って・データを入れて・つないで・正しく管理する」までやります。最後には15問の確認テストもあります。
🔄 振り返りチェック
- データベースとSQLの関係を、初めて聞く新入社員に1分で説明できますか?
WHEREとHAVINGの違いを、実行順序の観点で説明できますか?- 今日書いたSQLの中で、自分が一番「なるほど」と感じた1本を、なぜそう感じたか言葉にできますか?
補足資料
- 参考リンク: PostgreSQL: 日付・時刻関数
- 発展課題:
- 「注文ステータスごとの注文件数」を集計するSQLを書く
- 「2024年の各月、注文件数が3件以上の月だけ抽出」するSQLを書く(HAVING)
- 今日書いたSQLを
.sqlファイルにまとめ、コメントで「何のためのSQLか」を1行ずつ添える
学習ガイド
想定される質問と回答例
| 質問 | ヒント |
|---|---|
月別集計で DATE_TRUNC と EXTRACT どっちがいい? |
DATE_TRUNC('month', ...) は「2024-01-01 00:00:00」のような日時として返す。EXTRACT(MONTH FROM ...) は単に「1」「2」など月だけ。年も区別したいなら DATE_TRUNC または年・月を両方GROUP BYに入れる |
課題2で HAVING を WHERE COUNT(*) >= 3 と書いてエラー |
WHERE には集約関数を書けない。「集約後の絞り込みは HAVING」を改めて確認 |
| 集計結果に「(NULL)」が出た | GROUP BY 列に NULL がある場合、NULLは一つのグループとしてまとめられる |
ROUND で小数点を丸めると整数になる? |
ROUND(値, 0) は四捨五入して小数点なし。NUMERIC型で返るので表示はそのまま |
つまずきやすいポイント
| つまずきポイント | ヒント |
|---|---|
GROUP BY の列を SELECT から落としてしまう |
エラーにはならないが「何のグループか」が見えなくなる。基本は SELECT にも入れる |
WHERE で集約関数を使ってエラー |
集約は HAVING。実行順序を思い出す |
DATE_TRUNC の引数の順序を逆にする |
DATE_TRUNC('month', order_date) の順(単位が先、列が後) |
| 「2024年」のフィルタが片端しか効いていない | >= '2024-01-01' AND < '2025-01-01' のような半開区間が安全。BETWEEN '2024-01-01' AND '2024-12-31' だと12/31の時刻によっては漏れる |
| Day1の達成感で気が緩む | 明日のJOIN・設計が本番。「読めるようになった」だけでなく「変えられる・つなげる・作れる」を狙う |