データベースの正規化とは、同じデータを複数の場所に重複して持たないように、テーブルを分割していく設計の手順です。目的は、データを追加・更新・削除したときに、矛盾したデータが生まれないようにすることです。

実務では、多くの場合第3正規形まで正規化すれば十分とされています。この記事では、1枚の注文伝票を例に、非正規形から第1・第2・第3正規形へと、テーブルが分かれていく過程を順を追って解説します。あわせて、正規化しないと何が起きるのか、実務であえて正規化を崩すのはどんなときか、試験で問われやすいポイントまで紹介します。

正規化しないと起きる3つの異常

次の表は、注文の情報を1つの表にまとめたものです。

注文番号 注文日 顧客ID 顧客名 住所 商品ID 商品名 単価 数量
1001 9/1 C01 山田 東京都 P01 ノート 200 3
1001 9/1 C01 山田 東京都 P02 ペン 100 5
1002 9/2 C02 佐藤 大阪府 P01 ノート 200 1
1003 9/3 C01 山田 東京都 P03 消しゴム 80 2

一見わかりやすい表ですが、同じ情報が何度も出てきます(山田さんの住所が3回、ノートの単価が2回)。このため、次の3つの問題(異常)が起きます。

異常 例 起きること
更新時異常 山田さんが引っ越した 3行すべての住所を更新する必要がある。1行でも漏れると、どれが正しい住所かわからなくなる
挿入時異常 新しい商品を登録したい 注文がないと、商品だけを登録する行が作れない
削除時異常 注文1002を取り消した 佐藤さんの顧客情報まで消えてしまう

正規化は、この3つの異常をなくすための手順です。

正規化の前に:2つの用語

正規化の説明では、次の2つの言葉がよく出てきます。

  • 関数従属 — ある項目の値が決まると、別の項目の値が1つに決まる関係。「顧客IDが決まれば、顧客名が決まる」とき、「顧客名は顧客IDに関数従属する」と言い、「顧客ID → 顧客名」と書きます
  • 候補キー — その値が決まれば、行が1つに決まる項目(または項目の組み合わせ)。候補キーのうち、代表として選んだものが主キーです

正規化は、「どの項目が、どの項目に従属しているか」を整理し、従属する相手ごとにテーブルを分ける作業だと考えると理解しやすくなります。

非正規形:1つの行に繰り返しがある

紙の注文伝票をそのまま表にすると、1つの注文に複数の商品が並びます。

注文番号 注文日 顧客ID 顧客名 住所 商品(商品ID・商品名・単価・数量)
1001 9/1 C01 山田 東京都 P01・ノート・200・3 / P02・ペン・100・5
1002 9/2 C02 佐藤 大阪府 P01・ノート・200・1

1つのセルに複数の値が入っている(繰り返し項目がある)状態を、非正規形と呼びます。このままではSQLで「ペンを買った注文」を探すことも、数量を合計することも困難です。

第1正規形:1つのセルに1つの値

繰り返し項目をなくし、1つのセルに1つの値だけが入るようにした状態が第1正規形です。商品ごとに行を分けると、冒頭の表になります。

注文番号 注文日 顧客ID 顧客名 住所 商品ID 商品名 単価 数量
1001 9/1 C01 山田 東京都 P01 ノート 200 3
1001 9/1 C01 山田 東京都 P02 ペン 100 5
1002 9/2 C02 佐藤 大阪府 P01 ノート 200 1

この表の主キーは、「注文番号+商品ID」の組み合わせです(同じ注文の中で、同じ商品は1行)。

「電話番号1、電話番号2、電話番号3」のように、同じ種類の項目を列として並べるのも繰り返し項目の一種です。これも別のテーブル(または行)に分けるのが第1正規形の考え方です。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「データベースの正規化をわかりやすく|第1〜第3正規形の例」

第2正規形:主キーの一部だけに従属する項目を分ける

第1正規形の表で、各項目が何に従属しているかを整理します。

項目 何が決まれば決まるか
注文日・顧客ID・顧客名・住所 注文番号だけで決まる
商品名・単価 商品IDだけで決まる
数量 注文番号+商品IDの両方で決まる

主キーは「注文番号+商品ID」ですが、注文日や商品名は、主キーの一部だけで決まります。これを部分関数従属と呼びます。

部分関数従属をなくし、主キーの一部だけで決まる項目を別のテーブルに分けた状態が第2正規形です。

注文テーブル(主キー:注文番号)

注文番号 注文日 顧客ID 顧客名 住所
1001 9/1 C01 山田 東京都
1002 9/2 C02 佐藤 大阪府
1003 9/3 C01 山田 東京都

商品テーブル(主キー:商品ID)

商品ID 商品名 単価
P01 ノート 200
P02 ペン 100
P03 消しゴム 80

注文明細テーブル(主キー:注文番号+商品ID)

注文番号 商品ID 数量
1001 P01 3
1001 P02 5
1002 P01 1
1003 P03 2

これで、注文がなくても商品を登録できるようになり(挿入時異常の解消)、商品名の変更も1か所で済みます。

主キーが1つの項目だけでできている表は、部分関数従属が起こりえないため、第1正規形であれば自動的に第2正規形を満たします。

第3正規形:主キー以外の項目に従属する項目を分ける

第2正規形の注文テーブルを見ると、まだ山田さんの情報が繰り返されています。

  • 注文番号 → 顧客ID(注文が決まれば、顧客が決まる)
  • 顧客ID → 顧客名・住所(顧客が決まれば、名前と住所が決まる)

顧客名と住所は、主キー(注文番号)から、顧客IDを経由して間接的に決まっています。これを推移的関数従属と呼びます。

推移的関数従属をなくし、主キー以外の項目で決まる項目を別のテーブルに分けた状態が第3正規形です。

注文テーブル

注文番号 注文日 顧客ID
1001 9/1 C01
1002 9/2 C02
1003 9/3 C01

顧客テーブル

顧客ID 顧客名 住所
C01 山田 東京都
C02 佐藤 大阪府

これで、山田さんの住所の変更は1行だけで済み(更新時異常の解消)、注文を削除しても顧客情報は残ります(削除時異常の解消)。

最終的なテーブル構成

テーブル 主キー 項目
顧客 顧客ID 顧客名、住所
商品 商品ID 商品名、単価
注文 注文番号 注文日、顧客ID
注文明細 注文番号+商品ID 数量

1つの表が、「顧客」「商品」「注文」「注文明細」という、現実の物事の単位に分かれたことがわかります。正規化に慣れてくると、最初から「この項目は何についての情報か」を考えて、このような構成を設計できるようになります。

実務のポイント:注文時の単価は、注文明細にも持つ

ここで、実務で必ず問題になる点があります。上の構成では、単価は商品テーブルにしかありません。商品の値上げで単価を変更すると、過去の注文の金額まで変わってしまいます。

実務では、注文明細に「注文時の単価(販売単価)」を持たせるのが一般的です。

注文番号 商品ID 数量 販売単価
1001 P01 3 200

これは正規化に反しているように見えますが、そうではありません。商品テーブルの単価は「現在の定価」、注文明細の販売単価は「その注文のときの価格」で、意味の違う別のデータです。値引きや、時期によって変わる価格を正しく記録するためにも必要です。

同じ考え方で、配送先住所も、顧客テーブルの住所とは別に、注文ごとに持つのが一般的です。顧客が引っ越しても、過去の注文の配送先は変わらないからです。

「そのデータは、いつの時点の事実か」を考えることが、正規化を実務に活かすうえでの大切な視点です。

第3正規形より先の正規形

正規形 条件(概要)
ボイスコッド正規形(BCNF) すべての関数従属について、決定する側が候補キーである
第4正規形 互いに独立した複数の「多値」の情報を、1つのテーブルに持たない
第5正規形 それ以上、情報を失わずに分割できない状態

第3正規形のテーブルでも、候補キーが複数あって重なっている場合などに、まれに異常が残ることがあり、それを解消したのがボイスコッド正規形です。実務では、第3正規形(場合によってはボイスコッド正規形)までを目標にするのが一般的で、第4・第5正規形を意識する場面は多くありません。

あえて正規化を崩す:非正規化

正規化するとテーブルが増え、データを取り出すときにJOIN(結合)が必要になります。そのため、次のような場合には、あえて正規化を崩す(非正規化)ことがあります。

場面 非正規化の例 注意点
集計に時間がかかる 注文テーブルに「合計金額」を持たせる 明細を変更したら、合計も確実に更新する
表示のたびに多くのJOINが必要 一覧表示用に、必要な項目をまとめたテーブルを作る 元のデータとの同期の仕組みが必要
分析・レポート用のデータ データウェアハウスで、あえて横に広い表にする 更新ではなく、分析の速さを優先する用途

非正規化は、正規化した設計を理解したうえで、性能の問題が確認できたときに、理由を持って行うのが原則です。最初から崩した設計にすると、更新時の矛盾に悩まされることになります。

正規化の手順のまとめ

段階 やること 解消するもの
非正規形 → 第1正規形 繰り返し項目をなくし、1セル1値にする 繰り返し項目
第1正規形 → 第2正規形 主キーの一部だけで決まる項目を分ける 部分関数従属
第2正規形 → 第3正規形 主キー以外の項目で決まる項目を分ける 推移的関数従属

試験で問われやすいポイント

基本情報技術者試験・応用情報技術者試験・データベーススペシャリスト試験では、正規化が頻出です。

  • 第2正規形と第3正規形の違いを、「部分関数従属」と「推移的関数従属」という言葉で説明できるようにする
  • 表を見て、関数従属の関係(A → B)を書き出す練習をすると、どの正規形かを判定しやすくなる
  • 「第3正規形である」ためには、第2正規形の条件も満たしている必要がある(段階を飛ばせない)
  • 候補キーが複数ある問題では、非キー属性(どの候補キーにも含まれない項目)が何かを正しく見極める
  • データベーススペシャリスト試験では、ボイスコッド正規形や、関数従属から候補キーを求める問題も出題される

よくある質問

Q. 正規化はどこまでやればいいですか? A. 実務では、第3正規形までを目標にするのが一般的です。そのうえで、性能などの理由があれば、部分的に非正規化します。

Q. 正規化するとSQLが遅くなりませんか? A. JOINが増えるため、遅くなる場合もあります。ただし、適切なインデックスがあれば、多くの場合は問題になりません。遅くなった部分だけを、実行計画で確認してから対策するのが確実です。

Q. IDの列(サロゲートキー)を主キーにすれば、正規化は不要ですか? A. 不要にはなりません。IDを主キーにしても、同じ情報を複数の行に重複して持っていれば、更新時の異常は起きます。正規化は「どの情報が、何に従属しているか」を整理する作業で、主キーの選び方とは別の問題です。

Q. NoSQLでも正規化は必要ですか? A. NoSQLのデータベースでは、読み取りの速さを優先して、あえて重複を持たせる設計がよく使われます。その場合も、重複したデータをどう更新するかを、アプリケーション側で設計する必要があります。

まとめ

  • 正規化は、重複をなくして、更新・挿入・削除の異常を防ぐためのテーブル設計の手順
  • 第1正規形:繰り返しをなくす。第2正規形:主キーの一部だけで決まる項目を分ける。第3正規形:主キー以外の項目で決まる項目を分ける
  • 結果として、テーブルは顧客・商品・注文・注文明細のような、現実の物事の単位に分かれる
  • 注文時の単価や配送先は、現在の値とは別の事実として持つ
  • 実務は第3正規形が目標。非正規化は、性能の問題を確認してから、理由を持って行う

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「データベースの正規化をわかりやすく|第1〜第3正規形の例」

株式会社RJCでは、テーブル設計のような土台の技術を、未経験から入社したメンバーも含めて一緒に学んでいます。こうした知識を仲間と共有しながら、一緒に成長を楽しんでいます。

株式会社RJC ― SI事業・SES事業・AI駆動開発。未経験からエンジニアに育てるのが得意な会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?