応用情報 科目A #14:SQLの結合・集約・NULL
応用情報技術者試験の科目Aに必要なSELECT・WHEREと整列、内部結合と左外部結合、GROUP BY・HAVINGとCOUNT、NULLと3値論理、NOT INの落とし穴、DISTINCTを、固定データの追跡例とオリジナル演習問題5問で学びます。
AP科目A・第14回 · 2026年度・シラバスVer.7.2に基づく独立した学習資料です。IPAとの提携はなく、合格を保証しません。 シリーズ一覧を見る
IPAとの提携・公認関係がない独立学習資料である。過去問題及び第三者解説は使用せず、合格を保証しない。
学習インデックス
- 前回:AP13 — キーと正規化(今回は主キー・外部キーによる接続だけを利用)
- 今回:出題範囲 · 用語 · データ · SELECT · 結合 · 集約 · ON/WHERE · NULL · NOT IN · 重複 · 要点 · 問題 · 復習
- 次回:AP15 — トランザクション、ロック、ACID(今回は扱わない)
1. 公式の範囲
出典:IPA 応用情報技術者試験(レベル3)シラバス Ver.7.2, https://www.ipa.go.jp/shiken/syllabus/omgdg50000005kq5-att/syllabus_ap_ver7_2.pdf(2026-10-21確認、PDF 43~44頁・印刷フッター39~40頁)。
階層は、大分類3「技術要素」→中分類9「データベース」→2.データベース設計→(2)③データベースの論理設計(NULL)、及び3.データ操作→(1)データベースの操作、(2)②(b)データ操作言語(SELECT文)。SELECT/WHERE、結合、グループ化・集約、NULL、重複、整列を扱う。AP13からは正規化済みのキー接続だけを引き継ぎ、PDF 44頁(印刷フッター40頁)からのトランザクション処理はAP15へ回す。
2. 用語
| 韓国語 | 日本語 | 英語 |
|---|---|---|
| 선택 / 조건절 | 選択 / WHERE句 | selection / WHERE clause |
| 내부 조인 | 内部結合 | inner join |
| 왼쪽 외부 조인 | 左外部結合 | left outer join |
| 그룹화 / 그룹 조건 | グループ化 / HAVING句 | grouping / HAVING clause |
| 집계 함수 | 集約関数 | aggregate function |
| 널(NULL) | NULL(ヌル) | NULL |
| 3값 논리 | 3値論理 | three-valued logic (3VL) |
| 중복 제거 | 重複排除 | duplicate elimination |
| 정렬 | 整列 | ordering |
| 상관 서브쿼리 | 相関副問合せ | correlated subquery |
3. 固定データ
DEPARTMENT(dept_id PK, dept_name)と、NULL可の外部キーEMPLOYEE.dept_idを接続する。正規化は復習しない。
| dept_id | dept_name |
|---|---|
| 10 | Sales |
| 20 | Dev |
| 30 | HR |
| 40 | Legal |
| emp_id | emp_name | dept_id | bonus |
|---|---|---|---|
| 1 | Aki | 10 | 100 |
| 2 | Bo | 10 | NULL |
| 3 | Chika | 20 | 200 |
| 4 | Dai | NULL | 50 |
| 5 | Emi | 20 | NULL |
| 6 | Fumi | 30 | 100 |
BLOCKED_DEPT(dept_id nullable)は(20)と(NULL)だけを持つ。論理的な処理の理解順はFROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。これは意味上の手掛かりであり、DBMSの物理的実行順序ではない。
4. SELECT、WHERE、ORDER BY
SELECT emp_id, emp_name, bonus FROM EMPLOYEE
WHERE bonus >= 100
ORDER BY bonus DESC, emp_id ASC;
| emp_id | emp_name | bonus |
|---|---|---|
| 3 | Chika | 200 |
| 1 | Aki | 100 |
| 6 | Fumi | 100 |
WHEREはTRUEだけを残す。NULLとの比較はUNKNOWNなのでBoとEmiは除外される。第2整列キーが100の同順位を確定する。
5. INNER JOINとLEFT JOIN
SELECT e.emp_id, e.emp_name, d.dept_name
FROM EMPLOYEE AS e INNER JOIN DEPARTMENT AS d ON d.dept_id=e.dept_id
ORDER BY e.emp_id;
| emp_id | emp_name | dept_name |
|---|---|---|
| 1 | Aki | Sales |
| 2 | Bo | Sales |
| 3 | Chika | Dev |
| 5 | Emi | Dev |
| 6 | Fumi | HR |
内部結合はTRUEで一致する行だけを残す。DaiのNULL = 値はUNKNOWNである。
SELECT e.emp_id, e.emp_name, d.dept_id, d.dept_name
FROM EMPLOYEE AS e LEFT JOIN DEPARTMENT AS d ON d.dept_id=e.dept_id
ORDER BY e.emp_id;
| emp_id | emp_name | dept_id | dept_name |
|---|---|---|---|
| 1 | Aki | 10 | Sales |
| 2 | Bo | 10 | Sales |
| 3 | Chika | 20 | Dev |
| 4 | Dai | NULL | NULL |
| 5 | Emi | 20 | Dev |
| 6 | Fumi | 30 | HR |
左外部結合は左の全行を保存し、不一致の右列をNULLで補う。
6. GROUP BY、HAVING、COUNT
移植性のある標準的な書き方として、選択する非集約列をすべてGROUP BYに含める。
SELECT d.dept_id, d.dept_name, COUNT(*) AS joined_rows,
COUNT(e.emp_id) AS employee_count, COUNT(e.bonus) AS known_bonus_count
FROM DEPARTMENT AS d LEFT JOIN EMPLOYEE AS e ON e.dept_id=d.dept_id
GROUP BY d.dept_id, d.dept_name ORDER BY d.dept_id;
| dept_id | dept_name | joined_rows | employee_count | known_bonus_count |
|---|---|---|---|---|
| 10 | Sales | 2 | 2 | 1 |
| 20 | Dev | 2 | 2 | 1 |
| 30 | HR | 1 | 1 | 1 |
| 40 | Legal | 1 | 0 | 0 |
COUNT(*)は行を数え、COUNT(式)は式が非NULLの行だけを数える。LegalにはNULL補完行が1行あるため1,0,0になる。
SELECT d.dept_id, d.dept_name, COUNT(e.emp_id) AS employee_count
FROM DEPARTMENT AS d LEFT JOIN EMPLOYEE AS e ON e.dept_id=d.dept_id
GROUP BY d.dept_id, d.dept_name
HAVING COUNT(e.emp_id) >= 2 ORDER BY d.dept_id;
| dept_id | dept_name | employee_count |
|---|---|---|
| 10 | Sales | 2 |
| 20 | Dev | 2 |
WHEREはグループ化前の行、HAVINGは集約後のグループを絞る。HAVINGでの別名利用は製品差があるため集約式を繰り返した。
7. 右表の条件:ONとWHERE
ON内の条件は右側の一致を限定しつつ全4部門を保存する。
SELECT d.dept_id, d.dept_name, e.emp_name, e.bonus
FROM DEPARTMENT AS d LEFT JOIN EMPLOYEE AS e
ON e.dept_id=d.dept_id AND e.bonus >= 150
ORDER BY d.dept_id, e.emp_id;
| dept_id | dept_name | emp_name | bonus |
|---|---|---|---|
| 10 | Sales | NULL | NULL |
| 20 | Dev | Chika | 200 |
| 30 | HR | NULL | NULL |
| 40 | Legal | NULL | NULL |
SELECT d.dept_id, d.dept_name, e.emp_name, e.bonus
FROM DEPARTMENT AS d LEFT JOIN EMPLOYEE AS e ON e.dept_id=d.dept_id
WHERE e.bonus >= 150 ORDER BY d.dept_id, e.emp_id;
| dept_id | dept_name | emp_name | bonus |
|---|---|---|---|
| 20 | Dev | Chika | 200 |
後者は結合後に条件不成立行とNULL補完行を除く。このNULL拒否の右側述語は結果を内部結合のようにするが、「全てのWHEREが全てのLEFT JOINを内部結合にする」と一般化してはいけない。
8. NULLと3値論理
| 式 | 結果 |
|---|---|
100 >= 100 |
TRUE |
NULL >= 100 |
UNKNOWN |
TRUE AND UNKNOWN |
UNKNOWN |
FALSE AND UNKNOWN |
FALSE |
TRUE OR UNKNOWN |
TRUE |
WHEREとHAVINGはTRUEだけを残す。= NULLと<> NULLはNULL判定ではなくUNKNOWNになる。IS NULL、IS NOT NULLを使う。
SELECT emp_id, emp_name FROM EMPLOYEE
WHERE bonus IS NULL ORDER BY emp_id;
| emp_id | emp_name |
|---|---|
| 2 | Bo |
| 5 | Emi |
9. NULLを含むNOT IN
SELECT e.emp_id, e.emp_name FROM EMPLOYEE AS e
WHERE e.dept_id NOT IN (SELECT dept_id FROM BLOCKED_DEPT)
ORDER BY e.emp_id;
| emp_id | emp_name |
|---|---|
| (結果なし) |
x NOT IN (20,NULL)は概念上x<>20 AND x<>NULL。20はFALSE、他の値はUNKNOWN、従業員側NULLもUNKNOWNで、TRUEはない。
SELECT e.emp_id, e.emp_name FROM EMPLOYEE AS e
WHERE e.dept_id NOT IN
(SELECT dept_id FROM BLOCKED_DEPT WHERE dept_id IS NOT NULL)
ORDER BY e.emp_id;
| emp_id | emp_name |
|---|---|
| 1 | Aki |
| 2 | Bo |
| 6 | Fumi |
DaiのNULL NOT IN (20)はなおUNKNOWNである。
SELECT e.emp_id, e.emp_name FROM EMPLOYEE AS e
WHERE NOT EXISTS (SELECT 1 FROM BLOCKED_DEPT AS b
WHERE b.dept_id=e.dept_id)
ORDER BY e.emp_id;
| emp_id | emp_name |
|---|---|
| 1 | Aki |
| 2 | Bo |
| 4 | Dai |
| 6 | Fumi |
DaiにはTRUEの等値一致がないためNOT EXISTSはTRUE。所属不明を除く業務意図ならe.dept_id IS NOT NULLを加える。
SELECT e.emp_id, e.emp_name FROM EMPLOYEE AS e
WHERE e.dept_id IS NOT NULL
AND NOT EXISTS (SELECT 1 FROM BLOCKED_DEPT AS b WHERE b.dept_id=e.dept_id)
ORDER BY e.emp_id;
| emp_id | emp_name |
|---|---|
| 1 | Aki |
| 2 | Bo |
| 6 | Fumi |
二つのNOT EXISTSは異なる業務意図を表し、常に一方が唯一の代替ではない。
10. 重複、DISTINCT、並び順
SELECT dept_id FROM EMPLOYEE
WHERE dept_id IS NOT NULL ORDER BY dept_id;
| dept_id |
|---|
| 10 |
| 10 |
| 20 |
| 20 |
| 30 |
SELECT DISTINCT dept_id FROM EMPLOYEE
WHERE dept_id IS NOT NULL ORDER BY dept_id;
| dept_id |
|---|
| 10 |
| 20 |
| 30 |
SQLは指定しなければ重複を残す。ORDER BYなしの行順は未定義。NULLの既定位置とNULLS FIRST/LASTの構文・対応状況にはDBMS差がある。次は確定的な同順位キーを持つ移植可能なNULL-last例である。
SELECT emp_id, emp_name, bonus FROM EMPLOYEE
ORDER BY CASE WHEN bonus IS NULL THEN 1 ELSE 0 END,
bonus DESC, emp_id ASC;
| emp_id | emp_name | bonus |
|---|---|---|
| 3 | Chika | 200 |
| 1 | Aki | 100 |
| 6 | Fumi | 100 |
| 4 | Dai | 50 |
| 2 | Bo | NULL |
| 5 | Emi | NULL |
例はSQLiteで確定的に検証した。NULL・結合・COUNTの中核的意味は標準SQLだが、実行計画、照合順序・大文字小文字、NULL既定位置、一部構文はDBMSごとに異なる。
11. 早見表と落とし穴
| 目的 | 要点 |
|---|---|
| 行/グループの絞込み | グループ前はWHERE、後はHAVING |
| 左側の保存 | LEFT JOIN。一致条件は意図に応じONへ |
| 実体数 | 安易なCOUNT(*)でなくCOUNT(右キー) |
| NULL判定 | IS NULL / IS NOT NULL |
| 除外 | NULLの業務意図を定め、NULL入りNOT INに注意 |
| 重複/順序 | DISTINCT、明示的ORDER BYと同順位キー |
落とし穴:NULLを0・空文字とみなす、= NULL、非集約選択列をグループ化しない、HAVINGの別名に依存、論理順序を物理順序と誤認、DISTINCTが整列すると思う、行に自然順があると思う。
12. オリジナル練習問題
以下は全てオリジナルの4択問題(過去問題ではない)。選択肢はア/イ/ウ/エ。
問1 — SELECT/WHERE
4節の問合せが返す従業員IDの順はどれか。
- ア.
1,3,6 - イ.
3,1,6 - ウ.
3,1,2,5,6 - エ.
3,6,1
正解:イ(2)。 アはbonus降順を無視。イは200の後、100をID昇順にする。ウはNULLを含める。エは同順位を逆にする。
問2 — ON/WHERE
7節の二つの問合せを正しく比較した記述はどれか。
- ア. 両方ともDev/Chikaだけを返す。
- イ. WHERE版が4部門を保存する。
- ウ. ON版にLegal行はない。
- エ. ON版は4部門行、WHERE版はDev/Chikaだけを返す。
正解:エ(4)。 アはONでの左側保存を無視。イは逆。ウはLegalのNULL補完行を見落とす。エが追跡表どおり。
問3 — COUNT
Legalの(COUNT(*),COUNT(e.emp_id),COUNT(e.bonus))はどれか。
- ア.
(0,0,0) - イ.
(1,1,0) - ウ.
(1,0,0) - エ.
(1,0,1)
正解:ウ(3)。 アは補完行を忘れる。イはNULLのemp_idを数える。ウは1行と非NULL式0個を正しく数える。エはNULL bonusを数える。
問4 — NOT IN
9節最初の問合せの行数はどれか。
- ア. 0
- イ. 3
- ウ. 4
- エ. 5
正解:ア(1)。 アが正しい。イはNULL除去副問合せ、ウはガードなしNOT EXISTSの結果。エはデータに根拠がない。
問5 — 重複と順序
標準SQLのもとで正しい記述はどれか。
- ア. DISTINCTは必ず昇順に整列する。
- イ. 整列時の非NULL部門値は
10,10,20,20,30で、ORDER BYなしの順序は未定義。 - ウ. 従業員主キーは射影したdept_idの重複も防ぐ。
- エ. NULLは全DBMSで常に最後になる。
正解:イ(2)。 アは重複排除と整列を混同。イが正しい。ウはemp_idの一意性とdept_id射影を混同。エは製品差を無視。
正解(1-based):[2,4,3,1,2] = [イ,エ,ウ,ア,イ]。
13. 自己点検と復習計画
SQLを動かさず全結果表、Legalの1,0,0、NOT INの0行、Daiを含むかという業務意図、確定的NULL-lastを説明できるか確認する。
- 1日後:固定表を描き、WHERE、二つの結合、COUNTを手で追う。
- 3日後:ON/WHEREと三つの除外問合せをTRUE/FALSE/UNKNOWNで再現する。
- 7日後:5問を見ずに解き、GROUP/HAVING問合せと結果表を一つ書く。
出典・完全性:公式範囲には上記IPAシラバスだけを用いた。過去問題・第三者解説は使用していない。