第二正規形はキーの一部だけで決まる列を分ける

関係データベースの正規化で、第一正規形の次に置く段階が第二正規形 (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

この表では、列ごとに、何が分かれば値が決まるかが違う。

  1. 店舗名は 注文ID だけで決まる。注文 1 は明細が2行あるが、店舗名はどちらも東店である。
  2. 商品名は 商品コード だけで決まる。商品コード A01 は、どの注文でもりんごである。
  3. 数量は 注文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. 店舗名を直すときは、その注文の1行を直せばよい。明細が2行あっても、2行とも直す必要はない。
  2. 商品名を直すときは、商品の表の1行を直せばよい。
  3. まだ一度も注文されていない商品も、商品の表へ追加できる。
  4. ある注文の明細をすべて消しても、商品名は商品の表に残る。

ここまでの表では、キー以外の列は、その表のキーを見れば直接決まる。それでも、キー以外の別の列を経由して決まる重複は残ることがある。次の注文表は、行を区別する列が 注文ID だけなので、2NF である。

注文ID 店舗コード 店舗名
1 S10 東店
2 S20 西店
3 S10 東店

店舗コード S10 の注文は2件あり、東店が2行に書いてある。この店舗名は 注文ID からも辿れるが、同じ店舗コードなら店舗名は同じ、という見方のほうがはっきりしている。東店の表記を直すときは、S10 の注文をすべて直す必要がある。

注文ID が 店舗コード を決め、その 店舗コード が 店舗名 を決める。キー以外の列を経由して値が決まるこの関係を推移的関数従属と呼ぶ。第三正規形で分ける対象になる。2NF が整えるのは、キー以外の列が、行を区別するキー全体を見て決まるところまでである。