DB正規化 まとめ
はじめに
上記、参考書で正規化の基本的な方法について書かれていたので、纏めた。
個人的には思ったより、今まで業務の中で先輩方に教えていただいた事と相違がなかったので良い復習となった。
先に、個人的結論
正規化とは各エンティティを抽出し、1つのテーブルとして切り離すことでテーブルが持つデータが何のデータなのか明確にすること。
上記を守れば主キーと主キーに従属する従属列のみからなる、完全関数従属
なテーブルが出来上がる。
しかし、何でもテーブルを切り離して分割すれば良いということではない。処理時にテーブルの結合が頻繁に起こればパフォーマンスに影響を与える為、要件を鑑みて適切に判断することが大切。
実際、画面上で沢山の値を出す際には、テーブルの結合を避けて膨大な列を持っているテーブルに出会う事もあった。
正規化とは
データベースで保持するデータの冗長性を排除すること。
データの冗長性を排除
複数箇所に同じ内容のデータが存在することを無くす。あるデータを参照したり更新する時に目的のデータが複数箇所に存在すると、どの箇所のデータを参照、更新すべきなのか分からなくなる。また更新時にある箇所のデータのみ更新し忘れたりする可能性がある他、タイムラグによって一時的にデータの不整合が発生してしまう。
正規化のレベル
正規化のレベルには5つあるが、通常はレベル3である第三正規形まであれば通常の業務で対応できる。
第一正規形
・ 1セルには必ず1つの値のみ(スカラ値の原則
)
・ 繰り返しデータをカラムの列で管理しない。
・ RDMSでは標準機能に配列型を採用しているが基本的に使わない方が良い。
正規化前
case.1
社員番号が00001
の中田
社員の子カラムには英寿
と浩二
の2つの値が格納されてしまっている。
同様に社員番号が00002
の中村
社員の子カラムには俊輔
と憲剛
の2つの値が格納してある。
このままでは、中田英寿
さんを特定したい時に主キーである社員番号
でデータを引っ張っても中田英寿
さんだけでなく中田浩二
さんまで一緒に取得できてしまう。
社員番号 | 社員名 | 子 |
---|---|---|
00001 | 中田 | 英寿 浩二 |
00002 | 中村 | 俊輔 憲剛 |
00003 | 加藤 |
case.2
スカラ値は守られているが、子供をカラム列で管理してしまっている。子が増えれば子1,2,3に名前の値を入れていく運用となっている。
しかしこれでは子供が4人になったときにカラムを新たに追加する必要がある。さらに全てのレコードに影響を与えてしまうので、子がカラム数未満の場合は必然的にnull
、もしくはデフォルト値を入れていく必要がある。
社員番号 | 社員名 | 子1 | 子2 | 子3 |
---|---|---|---|---|
00001 | 中田 | 英寿 | 浩二 | |
00002 | 中村 | 俊輔 | 憲剛 | |
00003 | 加藤 |
正規化後
明確に社員
と子
テーブルに分割することでテーブルが格納しているデータが明確になった。
また各テーブルでスカラ値の原則
が守られている為、特定のデータを参照できるようになった。例えば中田英寿
さんのデータを取得したい場合、社員テーブル
と子テーブル
を社員番号
を条件に結合することで社員番号が00001
の子カラムの値が「英寿」さん
のみを取得できるようになる。
さらに今後子供が増えていっても子テーブルのレコードを足していけば良いだけなので、他のレコードに影響を与えることがない。またnull
値も不用意に増えることがない。
・ 社員テーブル
社員番号 | 社員名 |
---|---|
00001 | 中田 |
00002 | 中村 |
00003 | 加藤 |
・ 子テーブル
社員番号 | 子 |
---|---|
00001 | 英寿 |
00001 | 浩二 |
00002 | 俊輔 |
00002 | 憲剛 |
第一正規化のメリット
エンティティを分割管理することでエンティティが何のデータを保持しているのか明確になる。
子を持たない加藤さんでもNull
の子データを保持する必要がなくなる。主キーを社員番号と社員名の組み合わせ
、社員番号と子
の組み合わせで各テーブルで一意のデータを特定することが可能。
第二正規形
・ 部分関数従属を無くし、完全関数従属のみのテーブル構成にする。
正規化前
下記の社員テーブル
の主キーは会社コード
と社員ID
である。あくまで社員テーブルなので主キーの会社コード
と社員ID
の組み合わせに従属しているカラムのみで出来ていれば完全関数従属のみのテーブル
と言える。
しかし会社名
カラムに注目してみると会社コード
があれば会社名
が自然に特定されるので会社名
は会社コード
に従属しており、社員ID
には従属していない。(部分関数従属
)
・ 社員テーブル
会社コード | 会社名 | 社員ID | 社員名 | 年齢 |
---|---|---|---|---|
AAAA | AAA株式会社 | 00001 | 川口 | 35 |
AAAA | AAA株式会社 | 00002 | 中澤 | 34 |
AAAA | AAA株式会社 | 00003 | 加地 | 33 |
BBBB | BBB株式会社 | 00001 | 中澤 | 45 |
BBBB | BBB株式会社 | 00002 | 福西 | 39 |
正規化後
部分関数従属にあった会社コード
→会社名
の部分を会社テーブル
として分断させる。
分断後の社員テーブル
を見てみると会社コード
と社員ID
を主キーに、他の社員名
や年齢
は従属列として成立している。
同様に会社テーブル
も会社コード
を主キーに会社名
が従属列として成立しており、完全関数従属
なテーブルとなった。
・ 社員テーブル
会社コード | 社員ID | 社員名 | 年齢 |
---|---|---|---|
AAAA | 00001 | 川口 | 35 |
AAAA | 00002 | 中澤 | 34 |
AAAA | 00003 | 加地 | 33 |
BBBB | 00001 | 中澤 | 45 |
BBBB | 00002 | 福西 | 39 |
・ 会社テーブル
会社コード | 会社名 |
---|---|
AAAA | AAA株式会社 |
BBBB | BBB株式会社 |
第二正規化のメリット
正規化前と比べて会社テーブル
が独立しているので仮に社員情報が不明な新規の会社を簡単に追加する事ができる。正規化前の状態でこれを行おうとすると、社員情報が明確になるまで登録を待つ必要があるか、仮の社員情報を入れてレコードを挿入する必要があるかもしれない。
さらに、会社コード
がAAAA
なのに会社名
がBBB株式会社
のレコードも登録できてしまう。アプリケーションコードで制御をすれば問題ないが制御ロジックを書き忘れたりした場合、不整合なデータ登録ができてしまう。
なので会社コード
と会社名
の組み合わせは会社テーブル
で保管しておき、会社情報を取り扱う必要のある他のテーブルでは会社コード
を使い回すようにすれば上記のような不整合も起こらない。
第三正規形
・ 推移的関数従属を排除し、完全関数従属のみのテーブル構成にする。
正規化前
先ほどの正規化前のテーブルを用いて、さらに部署コードと部署名を追加して場合で考えてみる。
・ 社員テーブル
会社コード | 会社名 | 社員ID | 社員名 | 年齢 | 部署コード | 部署名称 |
---|---|---|---|---|---|---|
AAAA | AAA株式会社 | 00001 | 川口 | 35 | 01 | 営業 |
AAAA | AAA株式会社 | 00002 | 中澤 | 34 | 02 | 人事 |
AAAA | AAA株式会社 | 00003 | 加地 | 33 | 03 | 開発 |
BBBB | BBB株式会社 | 00001 | 中澤 | 45 | 01 | 経理 |
BBBB | BBB株式会社 | 00002 | 福西 | 39 | 02 | 総務 |
テーブルはあくまで社員テーブル
なので、この場合の主キーは会社コード
と社員ID
の複合キーとなる。
第二正規形では会社名
は会社コード
に従属しており、別テーブルである会社テーブル
として独立。
しかし上記のテーブルの場合、これだけでは主キーとそれに従属する従属列だけで構成された完全関数従属
のテーブルとは言えない。
部署名称
に注目すると、これは会社コード
と社員ID
に従属しておらず、どちらかと言えば部署コード
に従属している。さらに部署コード
は会社コード
に従属しており、関係性で言えば会社コード
→部署コード
→部署名
となる。(推移的関数従属
)
正規化後
会社コード
から見て推移的関数従属
の関係にあった部署コード
と部署名称
を切り離して部署テーブル
を作成。これで全てのテーブルが完全関数従属
のテーブルとなった。
・ 社員テーブル
会社コード | 社員ID | 社員名 | 年齢 |
---|---|---|---|
AAAA | 00001 | 川口 | 35 |
AAAA | 00002 | 中澤 | 34 |
AAAA | 00003 | 加地 | 33 |
BBBB | 00001 | 中澤 | 45 |
BBBB | 00002 | 福西 | 39 |
・ 会社テーブル
会社コード | 会社名 |
---|---|
AAAA | AAA株式会社 |
BBBB | BBB株式会社 |
・ 部署テーブル
会社コード | 部署コード | 部署名 |
---|---|---|
AAAA | 01 | 営業 |
AAAA | 02 | 人事 |
AAAA | 03 | 開発 |
BBBB | 01 | 経理 |
BBBB | 02 | 総務 |
第三正規化のメリット
第二正規化のメリットに通ずるものがあるが、こちらでも各エンティティが明確にテーブルに分けられたので、どんなデータが格納されているのか明確になったのと、仮に社員が一人もいない部署が出来たとしても部署テーブルにレコードを足せば良いだけなのでダミーの社員を作る必要がなくなった。
Discussion