学んでおくべきデータベース設計 ~正規化の功罪~

データベースについて学習をしていくと、必ずと言って良いほどデータベース設計の「正規化」について学ぶことになります。

データベースのメンテナンス性や生産性を高める設計手法として知られる正規化について、まだ知らない方は、そのメリットとデメリットについても把握しておきましょう。

そこで今回は、データベース設計~正規化の功罪~をテーマに、正規化について詳しくお伝えします。正規化について詳しく知りたいという方は、ぜひ参考にしてください。

正規化とは

そもそも正規化とは、データの重複を排除することで整合的にデータを取り扱えるようにデータベースを設計することを指します。

正規化してデータの重複を排除することができると、データベース上に「矛盾」が起きることを防げます。
設計段階からデータベースの正規化を行っておけば、
データの追加・削除・更新対応した際のデータの不整合やデータの消失などのさまざまなリスクを防ぎ、データベースのメンテナンス効率をアップすることができます。

ただし、正規化を行うことで管理の手間が掛かる可能性や、まとまった情報を得るために複数の情報をたどる必要が出てくる可能性があり、かえって面倒になるというリスクもあります。

第1正規化, 第2正規化, 第3正規化

具体的に正規化とは

『非正規形を第1正規形 → 第2正規形 → 第3正規形へと変形させること』を指します。その手順をそれぞれ「第1正規化」「第2正規化」「第3正規化」と呼びます。

それでは非正規形から第三正規形までの変化をみていきましょう。

非正規形

以下の表は『1つの項目に複数の値が入っている列』が存在しているため、非正規形に該当します。
この状態の表に対して段階的に正規化を行っていきます。

レシートID ユーザーID ユーザー名 商品ID 購入商品 購入数 合計購入数
1 10110 田中 A001 テレビ 2 4
A002 パソコンA 1
A003 ドライヤー 1
2 20110 鈴木 A004 パソコンB 1 1

第1正規化(非正規形 → 第1正規形)

第1正規化では、1つの項目の中には1つの値しか含まないようにするため、
主に、1つの項目に複数の値が入っている列を分割して独立させる作業を行います。

先程の非正規形では、
レシートID、ユーザーID、ユーザー名、合計購入数の列が結合されていましたので、
その部分のセルを独立させます。(エクセルの「結合を解除」と同じイメージです)
実際に第1正規化を行った表がこちらです。

レシートID ユーザーID ユーザー名 商品ID 購入商品 購入数 合計購入数
1 10110 田中 A001 テレビA 2 4
1 10110 田中 A002 パソコンA 1 4
1 10110 田中 A003 ドライヤー 1 4
2 20110 鈴木 A004 パソコンB 1 1

第2正規化…の前に用語の整理

第2正規化の説明には専門用語が多く出現するので、
先に第1正規化を行った表を見ながら用語の整理しましょう。

レシートID ユーザーID ユーザー名 商品ID 購入商品 購入数 合計購入数
1 10110 田中 A001 テレビA 2 4
1 10110 田中 A002 パソコンA 1 4
1 10110 田中 A003 ドライヤー 1 4
2 20110 鈴木 A004 パソコンB 1 1

候補キー

主キーと同様に、項目が決まれば表中のどの行を指しているかが特定できる項目です。
第2正規化が完了するまでは主キーは存在せず、主キー候補のことを候補キーと呼びます。
今回の例ではレシートIDと商品IDが候補キーとなります。

特徴としては、
・一意制約(行を一意に識別できること)
・行を特定するのに最小数の組み合わせであること
の2点です。

また、主キーとの相違点としては、候補キーには非NULL制約が無いことです。
※非NULL制約:NULLという値を許可しない

例えば、『レシートID』から『合計購入数』まですべてを指定すれば該当の行を特定することができますが、レシートIDと商品IDを指定するだけでもまったく同じ結果が得られます。
この時に指定する項目が最小のもの(レシートID + 商品ID)を候補キーと呼びます。
※尚、最小で無くとも『特定ができること』だけを満たしている組み合わせをスーパーキーと呼びます。

・関数従属

特定の項目が決まれば別の項目も決まるような関係性のことを指します。
購入商品は商品IDだけで特定することが出来るため、
商品IDは購入商品と関数従属な関係にあると言えます。

・部分関数従属

複数の候補キーが存在し、その一部に関数従属をしている状態を指します。
前述した関数従属の『特定の項目』が候補キーだった場合、それは部分関数従属だと言えます。
購入商品は商品ID(候補キー)だけで特定することが出来るため、
購入商品は商品IDと部分関数従属な関係にあると言えます。

・完全関数従属

複数の候補キーが存在し、その全てで関数従属をしている状態を指します。
購入数はレシートIDと商品IDが両方とも決まらないと特定が出来ないため、
購入数はレシートID + 商品IDと完全関数従属な関係にあると言えます。

・推移的関数従属

主キー以外の項目で関数従属をしている状態を指します。
ユーザー名はユーザーID(非主キー)だけで特定することが出来るため、
ユーザー名はユーザーIDと推移的関数従属な関係にあると言えます。

第2正規化(第1正規形 → 第2正規形)

第2正規化では
『第1正規形から部分関数従属を解消し、完全関数従属の状態にする』
という作業を行います。

具体的な解消方法は、表の分割です。
実際に第2正規化を行った表がこちらです。
分割前の第1正規形と見比べると分かりやすいですね。

▼分割前の表

レシートID ユーザーID ユーザー名 商品ID 購入商品 購入数 合計購入数
1 10110 田中 A001 テレビA 2 4
1 10110 田中 A002 パソコンA 1 4
1 10110 田中 A003 ドライヤー 1 4
2 20110 鈴木 A004 パソコンB 1 1

▼分割後の表

レシートID 商品ID 購入数
1 A001 2
1 A002 1
1 A003 1
2 A004 1
レシートID ユーザーID ユーザー名 合計購入数
1 10110 田中 4
2 20110 鈴木 1
商品ID 購入商品
A001 テレビA
A002 パソコンA
A003 ドライヤー
A004 パソコンB

第3正規化(第2正規形 → 第3正規形)

第3正規化では
『第2正規形から推移的関数従属を解消した状態にする』
という作業を行います。

主キー以外の項目で関数従属をしている状態を解消します。
つまり主キー以外の項目だけで他の項目が特定できる状態を解消する必要があります。

実際に第3正規化を行った表がこちらです。
ユーザーIDとユーザー名のみの表が新たに作られました。

レシートID 商品ID 購入数
1 A001 2
1 A002 1
1 A003 1
2 A004 1
レシートID ユーザーID 合計購入数
1 10110 4
2 20110 1
ユーザーID ユーザー名
10110 田中
20110 鈴木
商品ID 購入商品
A001 テレビA
A002 パソコンA
A003 ドライヤー
A004 パソコンB

正規化で防ぐ不整合

ここまで、正規化の概要や実施する内容について紹介してきました。
しかし、正規化の概要や実施する内容が分かっても、なぜ正規化をする必要性があるのか、
その理由が分からないと正規化を実施しようとは思わないですよね。

そこで、ここからは、その答えとなる
「正規化することでどのような不整合を防ぐことができるのか」について紹介していきます。

正規化によって防ぐことができる不整合は主に3つあります。

①修正不整合

同じ情報の項目が多く存在しているような冗長なデータの場合、
修正時に更新しなければならない項目が複数存在するため、
1か所でも修正を見逃すと存在しないはずのデータが残ってしまいます。

例えば、以下の表のユーザーID10110のユーザー名を田中から山田に変更する場合、
データの件数分の修正を行なう必要があります。
※この例の場合は3か所ですが、データが100万件存在する場合、100万か所の修正が必要です。

レシートID ユーザーID ユーザー名 商品ID 購入商品 購入数 合計購入数
1 10110 田中 A001 テレビA 2 4
1 10110 田中 A002 パソコンA 1 4
1 10110 田中 A003 ドライヤー 1 4
2 20110 鈴木 A004 パソコンB 1 1

②挿入不整合

データを入力する際に制約条件に違反してしまうことで発生する不整合です。
例えば、以下の表はレシートIDが主キーのため、ユーザーだけを追加することができません。
※(NULL, 99999, 山田, NULL)というタプルを挿入することはできません

レシートID ユーザーID ユーザー名 合計購入数
1 10110 田中 4
2 20110 鈴木 1

③削除不整合

データを削除する際に、必要な情報も同時に削除されることで発生する不整合のことを差します。
例えば、以下の表ではレシートIDが1のデータを削除すると
同時に田中というユーザーに関する情報も削除されてしまいます。

レシートID ユーザーID ユーザー名 合計購入数
1 10110 田中 4
2 20110 鈴木 1

これら3つの不整合の総称を「更新不整合」と呼びます。
こういった不整合を回避するために、正規化による適切な分割が必要になります

データベースは正規化しない方がよい?

ここまで正規化の必要性と具体的な手法についてみてきましたが、
実際のデータベース運用を考慮した上でも正規化は行うべきなのでしょうか。
正規化のメリット・デメリットを基に考えてみましょう。

メリット

正規化を実施すると、「正規化で防ぐ不整合」でも紹介したように、
修正・挿入・削除などのデータ更新時に生じる不整合を防ぐことができるため、
データ管理が容易になります。

また、データに共通性が生まれるためデータの移行がしやすく、
正規化によって不要なデータも削除されるため、容量の削減ができ、
処理効率が上がるというメリットもあります。

デメリット

「パフォーマンスの悪化」が考えられます。
なぜなら、正規化を行うとテーブルの分割が増えるため、
データ検索の際にテーブルを結合する必要性が発生するためです。

これらのメリット・デメリットを考慮した結論としては、
『基本的に正規化は行うべきだが、パフォーマンス向上を目的として意図的に非正規形を用いる場合もある』です。

まとめ

いかがでしたか。
今回は、データベースの正規化について紹介してきました。
初心者の方にとっては難しい内容もあるため、最初は理解するのに苦しむかもしれませんが、正規化はデータベース設計における基本となる内容です。
そのため、知識・スキルをしっかり身に着けておくようにしましょう。

今回紹介した内容を基にデータベースの正規化について学んで、実践で活躍できるプログラマーを目指しましょう!

よく読まれている記事

「技術について(開発言語/ツール情報)」でよく読まれている記事