2️⃣ STEP 9: 第2正規形(2NF)
部分関数従属性を排除して、データの冗長性をさらに減らそう
- 第2正規形(2NF)の定義と条件
- 関数従属性とは何か
- 部分関数従属性の問題点
- 第1正規形から第2正規形への変換
- 実践演習:第2正規形への正規化
学習時間の目安: 2.5時間 | 前提知識: STEP 1-8修了
🎯 1. 第2正規形(2NF)とは?
STEP 8からの続き:正規化の第二歩
STEP 8では、第1正規形(繰り返し項目の排除、アトミック性の確保)を学びました。このSTEP 9では、第2正規形を学び、部分関数従属性を排除していきます。
第2正規形の定義
第2正規形(Second Normal Form, 2NF)=第1正規形を満たし、かつ部分関数従属性がない状態
わかりやすく言うと、「主キーの一部だけに依存するカラムをなくす」というルールです。
注文詳細テーブルで主キーが「注文ID + 商品ID」の場合:
・「商品名」は「商品ID」だけで決まる(注文IDは不要)
→ これが「部分関数従属」で、第2正規形違反です。
第2正規形にするには、商品名を別テーブル(商品テーブル)に分離します。
第2正規形の条件
- 第1正規形を満たす:繰り返し項目なし、アトミック性、主キーあり
- 部分関数従属性がない:すべての非キー属性が、主キー全体に従属する
主キーが単一カラムの場合、自動的に第2正規形を満たします。
部分関数従属は、複合主キーの場合にのみ問題になります。
🔗 2. 関数従属性とは
関数従属性の基本
関数従属性(Functional Dependency)=「Aが決まればBも決まる」という関係
関数従属性の例
記法
⚠️ 3. 部分関数従属性とは
部分関数従属性の定義
部分関数従属性(Partial Functional Dependency)=複合主キーの一部だけに従属している状態
部分関数従属がある例(第2正規形違反)
- ❌ 商品名、単価が重複している(ノートPCが2回)
- ❌ 顧客名も重複(山田太郎が2回)
- ❌ 商品名を変更する場合、複数箇所を更新する必要(更新異常)
- ❌ ストレージの無駄
完全関数従属性
完全関数従属性(Full Functional Dependency)=主キー全体に従属している状態
🔄 4. 第1正規形から第2正規形への変換
変換ステップ
- 部分関数従属を特定:主キーの一部だけに従属するカラムを見つける
- 新しいテーブルを作成:部分関数従属するカラムを別テーブルに分離
- 元のテーブルから削除:分離したカラムを元のテーブルから削除
- 外部キーで関連付け:新しいテーブルとの関係を保つ
実例1:注文詳細テーブル
- 商品名、単価の重複がなくなった
- 顧客名の重複もなくなった
- 更新異常が解消
- ストレージ効率が向上
実例2:学生成績テーブル
💡 5. 第2正規形のポイント
主キーが単一カラムの場合
つまり:部分関数従属は、複合主キーの場合にのみ問題になります。
第2正規形にするメリット
同じデータの重複がなくなり、ストレージ効率が向上。
1箇所だけ更新すればよく、データの整合性が保たれる。
データ構造が整理され、理解しやすくなる。
📝 STEP 9 のまとめ
- 第2正規形:第1正規形 + 部分関数従属性がない
- 関数従属性:「Aが決まればBも決まる」という関係(A → B)
- 部分関数従属:複合主キーの一部だけに従属している状態(第2正規形違反)
- 完全関数従属:主キー全体に従属している状態(第2正規形OK)
- 第2正規形への変換:部分関数従属するカラムを別テーブルに分離
- 主キーが単一カラムなら、自動的に第2正規形を満たす
第2正規形は、複合主キーを持つテーブルで重要です。
チェックポイント:
✅ 主キーは複合キーか?
✅ 主キーの一部だけで決まるカラムはないか?
✅ あれば別テーブルに分離
次のSTEP 10では、第3正規形を学び、推移的関数従属を排除していきます!
📝 練習問題
第2正規形の定義を答えてください。
第1正規形を満たし、かつ部分関数従属性がない状態
つまり、すべての非キー属性が主キー全体に従属する(主キーの一部だけに従属しない)。
関数従属性「商品ID → 商品名」を日本語で説明してください。
「商品IDが決まれば、商品名も決まる」または「商品名は商品IDに関数従属する」
例:商品ID = 1 なら、必ず商品名 = ‘ノートPC’ のように一意に決まる。
主キーが単一カラム(例:user_id)の場合、部分関数従属は発生しますか?
いいえ、発生しません。
部分関数従属は「主キーの一部だけに従属する」状態です。主キーが単一カラムの場合、「主キーの一部」は存在しないため、自動的に第2正規形を満たします。
以下のテーブルは第2正規形を満たしていますか?理由も答えてください。
いいえ、第2正規形を満たしていません。
「単価」は「商品ID」だけで決まります(注文IDは不要)。つまり部分関数従属です。
改善案:単価を商品テーブルに分離する。
以下のテーブルを第2正規形に変換してください。
学生ID → 学生名、講座ID → 講座名・担当講師の部分関数従属を排除。
以下の関数従属性のうち、部分関数従属はどれですか?(主キー:注文ID, 商品ID)
A. (注文ID, 商品ID) → 数量 B. 商品ID → 商品名 C. 注文ID → 顧客名
B と C が部分関数従属
A:主キー全体に従属 → 完全関数従属
B:商品IDだけに従属 → 部分関数従属
C:注文IDだけに従属 → 部分関数従属
部分関数従属を残したまま(第2正規形にしない)とどんな問題が発生しますか?
1. 更新異常:商品名を変更する場合、複数行を更新する必要があり、更新漏れでデータ矛盾が発生。
2. 挿入異常:新商品を登録したいが、まだ誰も注文していない場合、登録できない。
3. 削除異常:注文を削除すると、商品情報も一緒に消えてしまう。
以下の要件を満たす、第2正規形のテーブルを設計してください。
【要件】病院の診察記録
・患者が医師の診察を受ける
・診察日、診察内容を記録
・医師には専門科目がある
診察IDを主キー(単一カラム)にすることで、自動的に第2正規形を満たす。
以下のテーブルを第2正規形に変換し、「すべての受講記録と学生名・講座名を取得するSQL」を書いてください。
第2正規形への変換:
SQLクエリ:
問題9で作成したテーブルを使って、「成績が80点以上の受講記録を、学生名と講座名付きで取得するSQL」を書いてください。
実行結果:
問題9で作成したテーブルを使って、「各学生の平均成績を計算するSQL」を書いてください。
実行結果:
ポイント:GROUP BYで学生ごとにグループ化し、AVGで平均成績を計算。LEFT JOINを使うことで、受講がない学生も含めることができる。
【総合問題】以下のテーブルを第2正規形に変換し、「各部署の売上合計を計算するSQL」を書いてください。
第2正規形への変換:
各部署の売上合計SQL:
実行結果:
ポイント:
✅ 部分関数従属(営業ID → 営業名・部署名、商品ID → 商品名)を排除
✅ GROUP BYとSUMで部署別集計
✅ JOINで必要な情報を結合
❓ よくある質問
Q1: 主キーが単一カラムなら、常に第2正規形を満たしますか?
はい、部分関数従属に関しては満たします。主キーが単一カラムの場合、「主キーの一部」は存在しないため、部分関数従属は起こりえません。ただし、第3正規形(推移的関数従属)の問題は残る可能性があります。
Q2: 複合主キーを避ければ、第2正規形を気にしなくていいですか?
はい、部分関数従属に関してはそうです。実務では、複合主キーを避けてサロゲートキー(ID)を主キーにすることが多いです。これにより自動的に第2正規形を満たします。
Q3: 第2正規形にすると、必ずパフォーマンスが悪くなりますか?
いいえ、必ずしもそうではありません。テーブルが小さくなりインデックスが効きやすくなる、更新が速くなるなどのメリットがあります。JOINが増えるデメリットはありますが、適切なインデックスを貼れば多くの場合は問題ありません。
Q4: 第1正規形と第2正規形、どちらが重要ですか?
両方とも重要ですが、第1正規形が最優先です。第1正規形がないと基本的なクエリすら書けません。第2正規形はデータの冗長性を減らす次のステップです。
Q5: 部分関数従属を見つけるコツはありますか?
「複合主キーの片方だけで決まるものはないか?」と自問してください。例えば、主キーが(注文ID, 商品ID)の場合、「商品IDだけで決まる値はないか?」「注文IDだけで決まる値はないか?」をチェックします。「商品名は商品IDだけで決まる」「顧客名は注文IDだけで決まる」などが典型的な部分関数従属です。
Q6: 正規化後、JOINでデータを取得するのが面倒です。良い方法はありますか?
ビュー(VIEW)を活用しましょう。よく使うJOINクエリをビューとして定義しておくと、あたかも1つのテーブルのように扱えます。また、ORM(Object-Relational Mapping)を使うと、プログラムからはJOINを意識せずにデータを取得できます。正規化されたテーブル構造を維持しつつ、利便性を確保する方法です。
学習メモ
データベース設計・データモデリング - Step 9