データベースの正規化(第三正規形まで)
はじめに
この記事はデータベースを習い始めた方向けに、正規化とは何なのかを具体例を交えて説明した記事となります。
正規化とはデータを扱いやすくするために整理することです。正規化の目的は、データの冗長性(データベースに同じデータが保持されている状態)を削減し、データベースの更新時のエラーを減少させることにあります。正規化には段階があり、通常の業務で使用されるのは第三正規形(3NF)までです。
第一正規形 1NF
第一正規形とは、テーブルの各セルに1つの値を持つ状態を指します。
スカラ値: 1つのセルに単一の値を持つとき、この値をスカラ値と呼びます。つまり、リストや配列ではない数値、文字列、日付などの単純な値です。
第一正規化の具体例
BEFORE
学生番号 | 名前 | 部活 |
---|---|---|
10001 | サトウ | 野球部 |
10002 | スズキ | 吹奏楽部 |
10003 | タナカ | テニス部、吹奏楽部 |
このテーブルは、学生番号10003の部活列に複数の値(テニス部、吹奏楽部)が含まれているため第一正規形ではありません。複数の値が含まれることで、データの更新や検索が複雑になります。
AFTER
学生番号 | 名前 | 部活 |
---|---|---|
10001 | サトウ | 野球部 |
10002 | スズキ | 吹奏楽部 |
10003 | タナカ | テニス部 |
10003 | タナカ | 吹奏楽部 |
各セルの値が単一となり、第一正規形を満たすようになりました。これにより、データの更新や検索が単純化されます。
これだけではまだ不十分
このテーブルには、まだ問題点があります。それは主キーを決められないことです。
主キー: 各レコードを一意に識別するために使用される列(または列の組み合わせ)。主キーは重複することがなく、NULL値を持つこともできません。
主キーがないと、同じデータが重複して保存されるリスクがあり、データの一意性を保証することができません。この問題を解決するために、次のようにテーブルを分離する必要があります。以下主キーにはPKをつけます。
学生番号 PK | 名前 |
---|---|
10001 | サトウ |
10002 | スズキ |
10003 | タナカ |
学生番号 PK | 部活 PK |
---|---|
10001 | 野球部 |
10002 | 吹奏楽部 |
10003 | テニス部 |
10003 | 吹奏楽部 |
第二正規形 2NF
第二正規形は、複合主キーを持つ第一正規形テーブルから部分関数従属を排除することで得られます。つまり、主キーを一つしか持たない第一正規形のテーブルは自動的に第二正規形の条件を満たします。
複合主キー: 複数の列から成る主キー
部分関数従属とは
意味がわからない言葉ですね。まずは関数従属について説明します。
関数従属とは関数y=f(x)のように、xに対してyが一意に定まる場合、「yはxに従属する」と表現します。「{X}→{Y}」とも表現します。先程のテーブルを例とするなら、名前は学生番号に従属すると言います。
学生番号 PK | 名前 |
---|---|
10001 | サトウ |
10002 | スズキ |
10003 | タナカ |
部分関数従属とは、非主キーの列が複合主キーの一部の列に対して従属している状態を指します。
第二正規化の具体例
BEFORE
学校コード PK | 学校名 | 学生番号 PK | 名前 | 部活コード | 部活 |
---|---|---|---|---|---|
A | 東 | 10001 | サトウ | 001 | 野球部 |
A | 東 | 10002 | スズキ | 002 | 吹奏楽部 |
A | 東 | 10003 | タナカ | 003 | テニス部 |
B | 西 | 10001 | タカハシ | 001 | 野球部 |
B | 西 | 10002 | ナカムラ | 004 | サッカー部 |
B | 西 | 10003 | ワタナベ | 002 | 吹奏楽部 |
このテーブルの主キーに関する従属関係は以下のようになります。
- {学校コード}→{学校名}
- {学校コード、学生番号}→{名前}
- {学校コード、学生番号}→{部活コード}
- {学校コード、学生番号}→{部活}
学校名を特定するためには、学生番号は不要で、学校コードのみが必要です。つまり、1は部分関数従属なので、テーブルから独立させます。
AFTER
学校コード PK | 学生番号 PK | 名前 | 部活コード | 部活 |
---|---|---|---|---|
A | 10001 | サトウ | 001 | 野球部 |
A | 10002 | スズキ | 002 | 吹奏楽部 |
A | 10003 | タナカ | 003 | テニス部 |
B | 10001 | タカハシ | 001 | 野球部 |
B | 10002 | ナカムラ | 004 | サッカー部 |
B | 10003 | ワタナベ | 002 | 吹奏楽部 |
学校コード PK | 学校名 |
---|---|
A | 東 |
B | 西 |
これにより、各テーブルの非主キーは複合主キー全体に従属するので、これらのテーブルは第二正規形と言えます。またこの状態を完全関数従属と呼びます。
第二正規化をしないと・・?
第二正規化とは、異なるエンティティをきちんとテーブルとして分離するプロセスです。正規化することによって、データの保守性が向上し、データの誤入力も防ぐことができます。
正規化をしない場合、以下のような問題が起こります。
- 学生情報が不明な学校コードCの南学校があった場合、正規化前のデーブルには登録することができません。主キーにはNULLをいれられないため。
- 正規化前のテーブルには{A, 東, …}というレコードの他に、{A, 北, …}という間違ったレコードが登録される可能性があります。
第三正規形 3NF
第三正規形は、第二正規形かつ、全ての非キーが主キーに直接関数従属している(推移的関数従属がない)状態です。
推移的関数従属とは
{X}→{Y}かつ{Y}→{Z}のような、段階的な従属関係のことを、推移的関数従属と言います。
第三正規化の具体例
BEFORE
学校コード PK | 学生番号 PK | 名前 | 部活コード | 部活 |
---|---|---|---|---|
A | 10001 | サトウ | 001 | 野球部 |
A | 10002 | スズキ | 002 | 吹奏楽部 |
A | 10003 | タナカ | 003 | テニス部 |
B | 10001 | タカハシ | 001 | 野球部 |
B | 10002 | ナカムラ | 004 | サッカー部 |
B | 10003 | ワタナベ | 002 | 吹奏楽部 |
学校コード PK | 学校名 |
---|---|
A | 東 |
B | 西 |
先程第二正規化したテーブルには、まだ従属関係が残っています。
{学校コード, 学生番号}→{部活コード}→{部活}
{部活コード}→{部活}の部分では、非主キーが非主キーに従属しているので、これを分離します。
AFTER
学校コード PK | 学生番号 PK | 名前 | 部活コード |
---|---|---|---|
A | 10001 | サトウ | 001 |
A | 10002 | スズキ | 002 |
A | 10003 | タナカ | 003 |
B | 10001 | タカハシ | 001 |
B | 10002 | ナカムラ | 004 |
B | 10003 | ワタナベ | 005 |
学校コード PK | 学校名 |
---|---|
A | 東 |
B | 西 |
部活コード PK | 部活 |
---|---|
001 | 野球部 |
002 | 吹奏楽部 |
003 | テニス部 |
004 | サッカー部 |
全ての非主キーは主キーに従属するので、これらのテーブルは第三正規形と言えます。これにより、所属学生がいない、部活を登録することができるようになりました。データの保守性が向上し、テーブル間の関係がより明確になりました。
おわりに
この記事では、データベースの正規化プロセスを第三正規形(3NF)までを説明しました。部分関数従属や推移的関数従属は複雑な概念ですが、具体例を通じて学ぶことで理解が深まると思います。