今日はリレーションナルデータベース論理設計における特に理解すべき概念ー「正規化」(Normalization)およびそれによって作られる「正規形」(normal form)について紹介致します。
正規形の定義
最初に、正規形について概略を説明します。
正規形(normal form)とは、一言で言うと、データベースで保持するデータの冗長性を排除し、一貫性と効率性を保持するためのデータ形式です。そして冗長性や非一貫性の問題を解決して正規形になるように考案された方法論は、正規化(normalization)と言います。
正規形には何段階かレベルがあって、一般的には下記の7つ正規形がよく知られています。
- 非正規形
- 第1正規形(1NF)
- 第2正規形(2NF)
- 第3正規形(3NF)
- ボイスコッド正規形(BCNF)
- 第4正規形(4NF)
- 第5正規形(5NF/PJNF)
正規化の中でも、通常の業務で使用するレベルとして、第3正規形までを考えるが多いため、第3正規形まで解説を行いますが、今回は第1正規形を中心にして説明いたします。
それでは、始めましょう。
第1正規形
まずは最もレベルの低い第1正規形からですが、実はリレーションナルデータベースのテーブルは、いずれも既に第1正規形を満たす形になっています。 第1正規形の定義とは「一つのセルの中は一つの値しか含まない」というものです。
「そんなの当たり前のことじゃないか」と思うかもしれませんが、現実に私たちが日常的に使う二次元表でな、こうやってきれいに「一つのセルに一つの値」という原則が守られているわけではありません。例えば、次の表を見てみましょう。
■非正規形(扶養者)
一人の社員は、複数の子を養っていることがあります。普通にやることで上記の表のように一つのセルに複数の値を入れ込む形で表現するが、これはリレーションナルデータベースでは重大な規則違反と見なされます。
上記のような表は、リレーションナルデータベースの世界では、次のどちらかの第1正規形に変換してやる必要があります。
■第1正規形(その1)
扶養者
■第1正規形(その2)
扶養者
その1は、子の数だけ列を増やしてやる方法、これに対してその2は、子の数だけ行を増やしてやる方法です。どちらも第1正規形に達成できます。(※「その1の形式は第1正規形ではない」と書いているテキストがありますが、これは間違いです。「一つのセルに一つの値」の原則を満たしているのですから、これも立派な第1正規形です。)
しかし、それぞれ問題があります。
その1の問題が多いですので、基本的は採用しない。(例えば、子供4人がいる新社員が入社すればテーブルの構造が変わなければならないとか)
その2のテーブルについても、このままだと二つの問題があります。まず一つ目は、このテーブルには主キーを決められないことです。このテーブルでは、1レコードの列をすべて特定しようとすると、{社員ID,社員名,子}全部の3列を指定せざるをえません。 {社員ID,社員名}だけだと、「社員ID=0001」、「社員名=橘」のような場合に、2レコードが該当してしまうからです。
しかし、問題はすべての社員が子を持っているわけではない。社員ID=0002の藤原さんは、子を持っていません。そのため、「子」列はNULLになります。ところが、主キーというのは定義上、その一部の列であっても、NULLにすることが許されません。
この問題を解決する方法として、子を持っていない社員の「子」列には、特定の文字列、例えば’子なし’を入れる、という業務ルールを作る方法もあります。これならば、テーブル構成を変更せずとも主キーを決められます。
しかし、この方法では、二つ目の問題を解決することができません。それは、このテーブルが意味的に「社員」と「扶養者」二つのエンティティの情報を含んでしまって、テーブルの意味やレコードの単位をするに理解できないことです。
上記二つの問題をともに解決するには、次のようなテーブルを分ける必要があります。
■第1正規形(その3)
社員
扶養者
「扶養者」テーブルについては子を持っている社員のレコードのみ限定して保持することで、主キーがNULLになることも防止できます。また、子を持っていない社員である藤原さんの情報が「扶養者」にいないですが、これについては、別に「社員」テーブルで管理しています。また、社員の名前と子の名前を結びつけたいときも、「社員」テーブルと「扶養者」テーブルとを社員IDをキーに結合すれば、簡単に得ることが可能です。
■「社員」テーブルと「扶養者」テーブルを内部結合する
結果:
■「社員」テーブルと「扶養者」テーブルを外部結合する
結果: