関係データベースの第二正規形を扱う
第二正規形はキーの一部だけで決まる列を分ける
関係データベースの正規化で、第一正規形の次に置く段階が第二正規形 (Second Normal Form, 2NF) である。
1NF では、1つのマスに値が1つだけ入っている。それでも、店舗名が明細の行数だけ繰り返される、といった重複は残る。2NF は、その重複を別の表へ移す。
行を他の行と区別する列の組を候補キーと呼ぶ。候補キーに入っていない列が、候補キーの一部だけで決まることを部分関数従属と呼ぶ。店舗名が 注文ID だけで決まる、というのがその例である。2NF は、この部分関数従属がない状態である。
候補キーが1列だけのときは、キーの一部だけで決まる列は出てこない。その表は 1NF であれば 2NF でもある。
注文表の繰り返しを決まる単位ごとに分ける
第一正規形の注文表では、1行を区別する候補キーが (注文ID, 商品コード) である。同じ注文の明細は、行を分けて書いてある。
| 注文ID | 店舗名 | 商品コード | 商品名 | 数量 |
|---|---|---|---|---|
| 1 | 東店 | A01 | りんご | 2 |
| 1 | 東店 | A02 | みかん | 1 |
| 2 | 西店 | A01 | りんご | 3 |
この表では、列ごとに、何が分かれば値が決まるかが違う。
- 店舗名は
注文IDだけで決まる。注文 1 は明細が2行あるが、店舗名はどちらも東店である。 - 商品名は
商品コードだけで決まる。商品コード A01 は、どの注文でもりんごである。 - 数量は
注文IDと商品コードが揃って初めて決まる。同じりんごでも、注文 1 では 2、注文 2 では 3 である。
店舗名は注文の表へ、商品名は商品の表へ移す。数量は注文と商品の組で決まるので、注文明細に残す。
注文の表では、注文ID が分かれば店舗名が1つに決まる。注文 1 の東店は、この1行にだけ書く。
| 注文ID | 店舗名 |
|---|---|
| 1 | 東店 |
| 2 | 西店 |
商品の表では、商品コード が分かれば商品名が1つに決まる。A01 のりんごは、注文のたびに書かなくてよい。
| 商品コード | 商品名 |
|---|---|
| A01 | りんご |
| A02 | みかん |
注文明細では、注文ID と 商品コード の組で行が1つに決まる。ここに残すのは、その組でないと決まらない数量だけである。
| 注文ID | 商品コード | 数量 |
|---|---|---|
| 1 | A01 | 2 |
| 1 | A02 | 1 |
| 2 | A01 | 3 |
3つの表を 注文ID と 商品コード でつなぐと、分ける前の行に戻る。注文 1 の商品 A01 は、店舗名が東店、商品名がりんご、数量が 2 になる。
第二正規形でも別の列を経由する重複は残る
表を分けると、値の直し方と追加の仕方が単純になる。
- 店舗名を直すときは、その注文の1行を直せばよい。明細が2行あっても、2行とも直す必要はない。
- 商品名を直すときは、商品の表の1行を直せばよい。
- まだ一度も注文されていない商品も、商品の表へ追加できる。
- ある注文の明細をすべて消しても、商品名は商品の表に残る。
ここまでの表では、キー以外の列は、その表のキーを見れば直接決まる。それでも、キー以外の別の列を経由して決まる重複は残ることがある。次の注文表は、行を区別する列が 注文ID だけなので、2NF である。
| 注文ID | 店舗コード | 店舗名 |
|---|---|---|
| 1 | S10 | 東店 |
| 2 | S20 | 西店 |
| 3 | S10 | 東店 |
店舗コード S10 の注文は2件あり、東店が2行に書いてある。この店舗名は 注文ID からも辿れるが、同じ店舗コードなら店舗名は同じ、という見方のほうがはっきりしている。東店の表記を直すときは、S10 の注文をすべて直す必要がある。
注文ID が 店舗コード を決め、その 店舗コード が 店舗名 を決める。キー以外の列を経由して値が決まるこの関係を推移的関数従属と呼ぶ。第三正規形で分ける対象になる。2NF が整えるのは、キー以外の列が、行を区別するキー全体を見て決まるところまでである。