仕上げは AI との協働でやっています。事実と表現は著者本人が確認しています。
ITパスポートのデータベース章で、いちばん詰まる場所はどこかと聞かれたら、迷わず「正規化」と答えます。第1正規形・第2正規形・第3正規形、と進む説明が、テキストだと数行で終わってしまうことが多いのですが、実際に手元で表を分解してみないと頭に入らないことが多いように思います。
研修で実際にホワイトボードに表を書きながら説明している図を、今回は記事として残しておきます。試験対策としても、SQL の JOIN を書くようになった時の足場としても効くと思います。
なぜ正規化が必要なのか、を先に置く
正規化の各段階を説明する前に、「正規化していない表で何が困るのか」を先に置きます。ここを飛ばすと「ルールがあるから従う」だけになって、試験の選択肢で迷います。
非正規形の表の例として、受講者管理の表を出します。
| 受講者ID | 氏名 | 受講コース1 | コース1講師 | 受講コース2 | コース2講師 |
|---|---|---|---|---|---|
| 001 | 田中 | ITパス対策 | 鈴木 | ネットワーク基礎 | 佐藤 |
| 002 | 山田 | データベース入門 | 高橋 | (空欄) | (空欄) |
| 003 | 林 | ITパス対策 | 鈴木 | データベース入門 | 高橋 |
この表で起きる問題は3つあります。
- コースの数に上限がある (列が固定なので、3つ目のコースを取りたい人を扱えない)
- 同じ情報が複数の行で重複している (鈴木講師の名前が複数箇所にあって、講師名を変更したい時に複数箇所を直す必要がある)
- データの一部が空欄になる (コース1しか取っていない人のコース2が空欄、集計時に扱いづらい)
正規化は、これらの問題を段階的に解消する分解作業として位置付けると、各段階の意味が掴みやすくなります。
第1正規形: 繰り返しを縦に伸ばす
第1正規形は 「繰り返し項目を行に展開する」 ことを指します。上の表の「コース1」「コース2」が繰り返し項目なので、これを行に分解します。
| 受講者ID | 氏名 | コース名 | 講師名 |
|---|---|---|---|
| 001 | 田中 | ITパス対策 | 鈴木 |
| 001 | 田中 | ネットワーク基礎 | 佐藤 |
| 002 | 山田 | データベース入門 | 高橋 |
| 003 | 林 | ITパス対策 | 鈴木 |
| 003 | 林 | データベース入門 | 高橋 |
これで「コースの数に上限がある」問題は解消されました。ただし、まだ残っている問題があります。
- 受講者氏名 (田中・林) が複数行で重複している
- 講師名 (鈴木・高橋) も複数行で重複している
ここから先が、第2・第3正規形の出番になります。
第2正規形: 主キーの一部にだけ依存するものを分ける
第1正規形の表で、主キーは {受講者ID, コース名} の組み合わせになっています (1人の受講者が複数コースを取れるので、受講者ID だけでは1行を特定できない)。
ここで、主キー全体ではなく一部にだけ依存する列がある場合、それを別の表に切り出すのが第2正規形です。
- 氏名は 受講者ID だけで決まる (コース名は関係ない) → 別表に分けるべき
- 講師名は コース名だけで決まる (受講者ID は関係ない) → 別表に分けるべき
分解すると以下になります。
受講者表
| 受講者ID | 氏名 |
|---|---|
| 001 | 田中 |
| 002 | 山田 |
| 003 | 林 |
コース表
| コース名 | 講師名 |
|---|---|
| ITパス対策 | 鈴木 |
| ネットワーク基礎 | 佐藤 |
| データベース入門 | 高橋 |
受講コース表 (受講者とコースの対応表)
| 受講者ID | コース名 |
|---|---|
| 001 | ITパス対策 |
| 001 | ネットワーク基礎 |
| 002 | データベース入門 |
| 003 | ITパス対策 |
| 003 | データベース入門 |
これで氏名と講師名の重複が消えました。ただし、「コース表」にまだ問題が残ることがあります。
第3正規形: 主キー以外の列に依存する列を分ける
仮にコース表が以下のように、講師の所属組織まで持っているとします。
| コース名 | 講師名 | 講師所属 |
|---|---|---|
| ITパス対策 | 鈴木 | A研修部 |
| ネットワーク基礎 | 佐藤 | B研修部 |
| データベース入門 | 高橋 | A研修部 |
| 基本情報対策 | 鈴木 | A研修部 |
ここで講師所属は コース名から直接決まるのではなく、講師名から決まる (講師名 → 講師所属、という従属関係)。主キー (コース名) 以外の列 (講師名) に従属している、という構造です。
これを別表に切り出すのが第3正規形です。
コース表 (第3正規形)
| コース名 | 講師名 |
|---|---|
| ITパス対策 | 鈴木 |
| ネットワーク基礎 | 佐藤 |
| データベース入門 | 高橋 |
| 基本情報対策 | 鈴木 |
講師表
| 講師名 | 講師所属 |
|---|---|
| 鈴木 | A研修部 |
| 佐藤 | B研修部 |
| 高橋 | A研修部 |
これで「鈴木講師がA研修部からC研修部に異動した」場合、講師表の1行を直すだけで済みます。第3正規形までやると、同じ情報が複数箇所に存在しない構造が完成します。
正規化の分解フロー
正規化は「表を段階的に分解していく作業」です。どの段階で何をしているのかをフローで整理します。
各段階で「何を除去しているか」に着目すると、第2・第3の区別が掴みやすくなります。
試験で聞かれる典型問題
ITパスポートでは「正規化を行った結果の表 (の組み合わせ) として適切なものはどれか」を選ばせる問題が出ます (シラバス上、正規化の詳細までは問われません)。「与えられた表が第何正規形か」を識別させる形式は応用情報やデータベーススペシャリストなど上位試験のものですが、どちらにせよ受講者が間違える順序は概ね決まっていて、「第2正規形と第3正規形が混ざる」ことが多いです。下の判定の質問順を押さえておくと、どちらの出題形式でも外しにくくなります。
混ぜないために以下の質問順で見ると外しにくいです。
この3問でツリー状に判定すると、試験の選択肢で迷う場面が減ります。
正規化と SQL の JOIN の話
正規化を進めると表が分かれるので、元の情報を取り出すには表をつなぐ操作が必要になります。これが SQL の JOIN です。
-- 受講者ID 001 の人がどのコースを取っていて、 講師は誰かを取り出す
SELECT 受講者.氏名, 受講コース表.コース名, コース.講師名
FROM 受講者
JOIN 受講コース表 ON 受講者.受講者ID = 受講コース表.受講者ID
JOIN コース ON 受講コース表.コース名 = コース.コース名
WHERE 受講者.受講者ID = '001';
正規化された表は更新時に整合性が保ちやすい一方で、取り出す時に JOIN を書く手間が増えます。これは「書き込みやすさ vs 読み出しやすさのトレードオフ」で、実務だと用途によって正規化を崩す (= 非正規化する) こともあります。例えばレポート用のテーブルは、最初から JOIN を済ませた状態で持っておく、という運用です。
試験対策としては第3正規形までで止まりますが、実務で触る時には「正規化が絶対的に正しいわけではなく、書き込み・読み出しの頻度で判断する」という視点も持っておくと、後で BigQuery や Redshift のような分析用 DB を触る時に位置付けやすくなります。
振り返って
データベース章の正規化は、ルールを覚えるだけでは選択肢で迷いやすいですが、「何が困るから分解するのか」を先に置くと、各正規形の意味が腑に落ちると思います。試験の選択肢を選ぶ時にも、上の3問判定で「何を見ているか」が明確になるので、ケアレスミスが減るのではないかと思っています。