テーブルの正規化について

半年ぶりに仕事をしていたらテーブルの正規化について忘れかけていたのでメモ。

正規化は基本的に第一正規化から第三正規化まで行う。それ以降のボイスコッド正規化、第四正規化、第五正規化についてはパフォーマンスに問題が出る場合が多いので基本的には意識しない。正規化を行えば行うほど、テーブルから冗長性を排除すればするほど、実用時にテーブルの再結合を行わなければいけなくなる場合が多いので、結局はパフォーマンスが犠牲になってしまう。

何故、正規化を行うのか

正規化とは、「1事実1カ所(1 fact in 1 place)を目指して、テーブルの整合性を保ったまま、テーブルの冗長性を排除して、データを効率的に管理できるようにすること」を目的とする。

仮にオンラインショップを運営すると仮定した場合にテーブルの正規化を行わないと以下の3つの問題が発生する。

  1. 事前登録不能(登録)

新しい商品を注文の前に登録できない。このテーブルにデータを登録するにはキーである注文番号、商品IDが必要なので、一度も注文されたことのない商品には注文番号が割り当てられていないため、登録ができない。
 
2. 重複更新(更新)

商品の名前や単価が変更されたとき、複数ある同一商品の情報をすべて更新しなくてはならない。同じ商品の情報が注文ごとに繰り返し登録されているため、1つの商品情報を更新するためには複数行を残らず更新する必要がある。
 
3. 関係喪失(削除)

一度しか注文されたことのない商品を含む注文が削除されると、商品情報が失われてしまう。注文を削除するという行為により、商品が削除されてしまうことがある。
 
上記のような問題を避ける為にも正規化は必要である。

第一正規形でやること

1つのフィールドに1つの情報を入れることで第一正規形となる。

※例えば「氏名」というカラムの1つのフィールドに「田中太郎」と「鈴木裕太」という2つの名前が入っている場合は第一正規形にはなっていないということである。

第二正規形でやること

第二正規形は「部分関数従属が存在しないもの」のことを言う。この表では「顧客ID」と「商品ID」が分かれば、顧客が注文した「数量」が分かり、「商品ID」が分かれば、「単価」が分かることになる。

顧客ID 商品ID 単価 数量
A001 01-192 300 100
A001 01-16 100 500
A003 02-103 1000 60
B004 02-103 1000 40

このテーブルでは「商品ID → 単価」「顧客ID/商品ID → 数量」が成立する。この2つはどちらも「商品ID」が矢印の左側にある為、部分関係従属であると言える(2つのうち片方だけならば問題無し)。部分関係従属を解消するにはテーブルを分割する必要がある。

上記の表を下記のように分割することで第二正規形となる。

顧客ID 商品ID 数量
A001 01-192 100
A001 01-16 500
A003 02-103 60
B004 02-103 40
商品ID 単価
01-192 300
01-16 100
02-103 1000

これで部分関数従属が無くなり、自由に商品の追加や単価を変更出来るようになった。

第三正規形でやること

第三正規形は「推移関数従属が存在しないもの」のことを言う。
この表では「学籍番号」が分かれば、「氏名」が分かり、「学籍番号」が分かれば、「サークルID」が分かり、「サークルID」が分かれば「サークル名」が分かることになる。これを推移関数従属という。

学籍番号 氏名 学部 性別 サークル
xxxx0001 A山B太 工学部 サッカー
xxxx0002 C田D子 文学部 コーラス

このテーブルでは「学籍番号 → サークルID」,「サークルID → サークル名」が成立する。この関係は推移関数従属であると言える。

上記の表を下記のように分離することで第三正規形となる。

学籍番号 氏名 サークルID
xxxx0001 A山B太 S039
xxxx0002 C田D子 S138
サークルID サークル名
S039 サッカー
S138 コーラス

これで推移関数従属が無くなり、サークルを自由に追加・更新することが出来るようになった。

まとめ

  • 第二正規形ではテーブル内の部分関数従属を無くす。
  • 第三正規形ではテーブル内の推移関数従属を無くす。
  • レコードを挿入・更新・削除するときにカラムの構造に不都合が無いかを考えることで、部分関数従属や推移関数従属を見つけることが出来る。

参考
正規形・正規化(第一正規形~第三正規形)
素早く正規形を見抜く実践テクニック
書籍 達人に学ぶDB設計 徹底指南書 初級者で終わりたくないあなたへ