ON 句と USING 句難易度 ★★★★☆
ON と WHERE のどちらに書くか
すべての部署について、2020 年 1 月 1 日以降に入社した社員の人数を求めてください。該当する社員が 1 人もいない部署も、人数 0 として結果に含めてください。列は「busho_name, ninzu」の順、人数の多い順、人数が同じ場合は部署名の昇順に並べてください。
この書き方が使えない製品があります そのまま使える: PostgreSQL / MySQL / MariaDB / IBM Db2
- SQLite では使えません(DATE 'YYYY-MM-DD' の日付リテラル)
- SQL Server では使えません(DATE 'YYYY-MM-DD' の日付リテラル)
- Oracle Database では使えません(DATE 'YYYY-MM-DD' の日付リテラル)
- ON に条件を足すと、LEFT JOIN で「条件に合う相手だけを結合する」ことができます。同じ条件を WHERE に書くと、相手がいない行ごと消えて内部結合と同じ結果になります。
- 模範解答の DATE 'YYYY-MM-DD' という日付リテラルは、SQLite と SQL Server では使えません。'2025-01-01' と文字列で書くか、CAST('2025-01-01' AS DATE) と書きます(Oracle も DATE リテラルはありますが書式が異なります)。
● 起動中…
この問題で使えるテーブル(名前をタップすると入力できます)
7 行
| 列 | 型 | NULL |
|---|---|---|
| 🔑 | int | 不可 |
| text | 不可 | |
| int | 可 |
データを見る(先頭 5 行)
| busho_id | busho_name | parent_busho_id |
|---|---|---|
| 1 | 経営 | NULL |
| 2 | 開発 | 1 |
| 3 | 営業 | 1 |
| 4 | 管理 | 1 |
| 5 | 基盤 | 2 |
18 行
| 列 | 型 | NULL |
|---|---|---|
| 🔑 | int | 不可 |
| text | 不可 | |
| int | 可 | |
| int | 可 | |
| numeric(10,0) | 不可 | |
| date | 不可 | |
| date | 可 |
データを見る(先頭 5 行)
| shain_id | name | busho_id | joushi_id | salary | hired_on | taishoku_on |
|---|---|---|---|---|---|---|
| 1 | 鈴木 | 1 | NULL | 980000 | 2014-04-01 | NULL |
| 2 | 佐藤 | 2 | 1 | 720000 | 2016-10-01 | NULL |
| 3 | 田中 | 2 | 2 | 520000 | 2019-04-01 | NULL |
| 4 | 中村 | 2 | 2 | 530000 | 2020-04-01 | NULL |
| 5 | 小林 | 2 | 2 | 445050 | 2024-04-01 | NULL |
解説
結論
外部結合で「残したい側」を保ちたいなら、相手側への条件は ON に書きます。
★なぜ WHERE では消えるのか: 外部結合は「まず結合し、相手がいなければ相手側の列を NULL で埋める」処理です。該当者が 0 人の部署(経営・法務)は s.hired_on が NULL になります。ここで WHERE s.hired_on >= DATE '2020-01-01' と書くと、NULL との比較結果は真でも偽でもなく不明なので、WHERE はこの行を通しません。結果として行ごと捨てられ、外部結合が実質的に内部結合と同じになります。
ON に書けば条件は「結合の段階」で適用され、相手が見つからなくても左側の行は残ります。
⚠️ 例外に注意
条件が IS NULL の場合は NULL に対して真になるので行は消えません。つまり「WHERE に書くと必ず消える」わけではなく、NULL に対して真にならない条件(比較・等値・IS NOT NULL)のときに消えるのが正確な理解です。
よくある間違い
count(*) を使う。外部結合では相手がいない行も 1 行として存在するので、該当者 0 人の部署が 1 人になります。人数を数えるときは必ず相手側の列を渡してください。
根拠(一次情報)
- PostgreSQL 18 マニュアル: 結合表一次情報・確認 2026-07-26