【DB設計】正規化の基礎
はじめに
「SQL〜ゼロからはじめるデータベース操作〜」と「達人に学ぶSQL徹底指南書」でSQL文の基礎は学習したので、DB設計も学習したい思い「達人に学ぶDB設計徹底指南書」を読みました。
今回はその中でも正規化についてまとめてみようと思います。
正規化とは
DB設計を行き当たりばったりで行うと、以下のような問題が起きることがあります。
・一つの情報が複数のテーブルに存在して、無駄なデータ領域と面倒な更新処理を発生させてしまう。(冗長性)
・冗長なデータを保持することで、更新処理のタイムラグによってデータの不整合が発生したり、そもそもデータを登録できないテーブルを作ってしまう。(非一貫性)
このような冗長性を排除し、一貫性と効率性を保持するための方法が正規化です。
また、正規化によって作られるテーブル設計を正規形と言います。
正規形のレベルは第5まであるそうですが、今回は通常の業務で使用するレベルとして第3正規形までをまとめます。
第1正規形〜スカラ値の原則〜
第1正規形の定義は簡単で、「一つのセルの中には一つの値しか含まない」というものです。
では、なぜ一つのセルに複数の値を入れることが認められていないのでしょうか?
答えは、セルに複数のキーを許すと主キーが各列の値を一意に決定できないからです。
主キーとは各列の値を一意に決めることができるものなので、これは主キーの定義に反しています。
この、Xに対してYが一つに決まる関係を関数従属性といい、
{X} → {Y}
と表現します。
第2正規形〜部分関数従属〜
第2正規形の定義は、「テーブル内で部分関数従属を解消し、完全関数従属のみのテーブルを作る」ことです。
主キーの一部の列に対して従属する列がある場合、この関係を部分関数従属と呼びます。
社員テーブル
例えば、{ 会社コード, 社員ID }が主キーの上のような「社員テーブル」があるとします。
このとき、他のすべての列はこのキーに従属するものの、「会社名」列だけが主キーの一部である「会社コード」列に従属しています。
つまり、以下のような関数従属があるということです。
{会社コード} → {会社名}
このような状態のことを部分関数従属と呼びます。
このテーブルに対して第2正規化を行うには、次のように部分関数従属の関係にあるキー列と従属列だけ独立のテーブルにすれば良いです。
社員テーブル
会社テーブル
これによって、「社員テーブル」も「会社テーブル」も、すべての列が主キーに完全関数従属することになりました。
では、なぜ部分関数従属を解消する必要があるのでしょうか?
例えば、第2正規化する前の「社員テーブル」に、社員の情報が不明の会社(C建設)を登録することになった場合を考えてみましょう。
すると、主キーの一部に社員IDが含まれているため、これが不明(NULL)の状態ではレコードを登録できないという問題が起こることがわかります。
第2正規化は、「会社」と「社員」という異なるレベルの実体をテーブルとして分離する作業
また、第2正規化したテーブルは正規化する前の状態に必ず戻せる(可逆性)ようになっています。
具体的には、以下のようなSQL分で内部結合することで戻せます。
第2正規化に可逆性があるのは、正規化によって失われる情報がないからです。
このように情報を完全に保存したままテーブルを分解することを、無損失分解といいます。
第3正規形〜推移的関数従属〜
改めて第2正規化まで行ったテーブルを見てみましょう。
かなり不都合を防止できる形にはなりましたが、実はまだ不都合が起きるケースがあります。
社員テーブル
会社テーブル
例えば、社員が一人もいない部署をテーブルに追加することを考えてみましょう。
すると、先ほど同じく社員IDが主キーの一部であるため、そこがNULLのままレコードを登録できないという問題が起きることがわかります。
この二つの列の間には、
{部署コード} → {部署名}
という関数従属が成立します。
一方、社員コードと部署コードの間にも、
{会社コード, 社員ID} → {部署コード}
という関数従属が存在しています。
つまり全体として、
{会社コード, 社員ID} → {部署コード} → {部署名}
という二段階の関数従属があるのです。
このように、テーブル内に存在する段階的な従属関係のことを、推移的関数従属と呼びます。
この推移的関数従属によるデータ登録時の不都合を解消するには、第2正規化の時と同じように、テーブルを分割することで、それぞれの関数従属の関係を独立させます。
社員テーブル
会社テーブル
部署テーブル
これで、データ登録時の不都合はなくなり、推移的関数従属もなくなりました。
社員が一人もいない部署も、「部署テーブル」に登録することができます。
異なるレベルの実体をテーブルとして分離するという意味では、第2正規化と同じ意味を持っています。
また、第3正規化も無損失分解です。
まとめ
ポイント1:正規化とは、更新時の不都合/不整合を排除するために行う
ポイント2:正規化は、従属性を見抜くことで可能になる
ポイント3:正規化は無損失分解なので、正規形はいつでも非正規形に戻せる(可逆性)