基礎正規化

なぜ正規化が必要か

定義

正規化とは、更新時異常 (挿入異常・更新異常・削除異常) を排除するために、リレーションを関数従属性の分析に基づいて分割し、一つの事実を一つの場所にのみ格納するデータベース設計手法である。

同じ情報を何度も書くと何が困るのか

こんな表を想像してほしい。従業員 1 人につき 1 行、「氏名 / 部署ID / 部署名 / 部署所在地」を書くタイプの表。 同じ部署に何人か所属していると、その 部署の名前や所在地が人数分だけ何度も書かれる ことになる。

従業員_部署
社員ID氏名部署ID部署名部署所在地
E001山田D01営業部東京
E002田中D01営業部東京
E003佐藤D02開発部大阪
E004鈴木D02開発部大阪

この「同じことを何度も書いている」状態は、ぱっと見は問題なさそうでも、 いざデータを追加 / 変更 / 削除しようとすると 3 種類の困りごとを引き起こす。 これらをまとめて 更新時異常 (update anomalies) と呼ぶ。

  • 挿入異常 — 追加したいのに追加できない
  • 更新異常 — 一部だけ書き換えて矛盾が起きる
  • 削除異常 — 消したい情報と一緒に、消したくない情報も消える

まずは各異常が「実際どんな場面で起きるか」を見てみる。 以下 3 つの図はコマ送りで進められるので、「次へ」を押しながら順に見てほしい。

1. 挿入異常 — 追加したいのに追加できない

挿入異常 — 所属者のいない新部署を登録できない
STEP 1 / 3
従業員_部署
社員ID氏名部署ID部署名部署所在地
E001山田D01営業部東京
E002田中D01営業部東京
E003佐藤D02開発部大阪
E004鈴木D02開発部大阪
?????
INSERT INTO 従業員_部署
(社員ID, 氏名, 部署ID, 部署名, 部署所在地)
VALUES (NULL, NULL, 'D03', '人事部', '東京');
STEP 1 / 3

初期状態: D03 (人事部) はまだ存在しない

なぜこの異常が起きるか

「D03 = 人事部 (東京)」を登録したくても、 このテーブルでは 社員行としてしか事実を書けない。 社員が居ないと社員ID を NULL にせざるを得ず、主キー制約に引っかかって登録できない。 「部署」という事実の格納先がそもそも独立して存在していないことが根本原因。

2. 更新異常 — 一部だけ書き換えて矛盾が起きる

更新異常 — 部署名の変更が全行に伝わらない
STEP 1 / 3
従業員_部署
社員ID氏名部署ID部署名部署所在地
E001山田D01営業部東京
E002田中D01営業部東京
E003佐藤D02開発部大阪
E004鈴木D02開発部大阪
UPDATE 従業員_部署
SET 部署名 = 'セールス部'
WHERE 社員ID = 'E001';
STEP 1 / 3

初期状態: D01 の 2 行はどちらも「営業部」

なぜこの異常が起きるか

「D01 = 営業部」という 1 つの事実が、社員の数だけ複製されている。 改称のためには D01 を持つ 全行 を漏れなく更新する必要があり、 1 行でも忘れると同じ D01 が「営業部」と「セールス部」の 2 つの部署名を持つ矛盾状態になる。 「部署」の事実が独立していれば 1 行の UPDATE で済むはずのもの。

3. 削除異常 — 消したくない情報も一緒に消える

削除異常 — 最後の所属者を消すと部署情報も消える
STEP 1 / 3
従業員_部署
社員ID氏名部署ID部署名部署所在地
E001山田D01営業部東京
E002田中D01営業部東京
E003佐藤D02開発部大阪
E004鈴木D02開発部大阪
DELETE FROM 従業員_部署 WHERE 社員ID = 'E003';
DELETE FROM 従業員_部署 WHERE 社員ID = 'E004';
STEP 1 / 3

初期状態: D02 (開発部/大阪) には 佐藤 (E003)・鈴木 (E004) の 2 名

なぜこの異常が起きるか

「D02 = 開発部 (大阪)」という 部署そのものの事実 は、 このテーブルでは「社員行」の中にしか存在していない。 最後の所属者を消した瞬間、部署情報も一緒に失われる。 後で新しい社員を D02 に配属したくても、部署名や所在地はもう分からない。

解決の指針: 「同じ情報は 1 か所にだけ書く」

3 つの異常はいずれも 同じ情報を複数の場所に書いている ことが原因。 逆に言えば、「同じ情報は 1 か所にだけ書く」 と決めれば、これらは自然に解消する。 これが正規化の一番大事な指針で、教科書では「一事実一箇所」(One Fact in One Place) と呼ばれることも多い。

具体的にはどうするか。 「部署」の情報 (部署ID・部署名・所在地) は本来「部署ごとに 1 つ」の情報なので、部署テーブルを別に作って部署の情報はそこにだけ書く。 従業員テーブルには「どの部署か」を示す 部署ID だけを持たせて、 部署の詳細を知りたい時は部署テーブルを見に行けばいい。

従業員
社員IDPK氏名部署ID
E001山田D01
E002田中D01
E003佐藤D02
E004鈴木D02
部署
部署IDPK部署名部署所在地
D01営業部東京
D02開発部大阪

部署の名前も所在地も「D01 は営業部で東京」「D02 は開発部で大阪」と、それぞれ 1 か所にしか書かれていない。 従業員テーブルには 部署ID という参照だけが残る。

こうすると:

  • 更新異常が消える: 部署名を変える時は部署テーブルの 1 行を書き換えるだけで全従業員の表示に反映される。「一部だけ書き換わって矛盾」が起きようがない。
  • 挿入異常が消える: 従業員が 1 人もいない新部署も、部署テーブルに 1 行足せば登録できる。
  • 削除異常が消える: 従業員が全員辞めても、部署の情報は部署テーブルに残っているので消えない。

正規化はこの指針を「機械的に」進める手順

「同じ情報を 1 か所に書く」と言われても、実際どうテーブルを分ければいいか迷う。 そこで先人たちは、「どの列がどの列を決めているか」という関係 (関数従属性) を見れば機械的に分割できる、という手順を作った。 これが 第1正規形第2正規形第3正規形と呼ばれる正規化の各ステップ。

実務では 第3正規形まで 進めれば十分とされることが多い。 それより先 (BCNF・4NF・5NF) は現実の業務モデルでは滅多に問題にならないため、まずは 3NF を目標にすればいい。 逆に、性能上の理由で「わざと同じ情報を書いておく」判断もあり、これを非正規化 と呼ぶ。

よくある疑問

Q.正規化しないとどんな問題が起きますか?
A.同じ情報が複数行に重複し、更新漏れ・削除時の情報消失・関連情報が無いと挿入できないといった「更新時異常」が発生します。データの信頼性が保てなくなります。
Q.更新異常・挿入異常・削除異常の違いは?
A.更新異常は同じ事実の一部だけ書き換わって矛盾する状態、挿入異常は関連する主体が存在しないと事実を登録できない状態、削除異常はある行を消すと別の必要な情報まで消える状態を指します。
Q.どこまで正規化すべきですか?
A.実務では第3正規形まで正規化するのが標準的な指針です。BCNF 以上はスキーマの複雑度と参照コストが上がるため、必要になった時だけ検討します。
Q.正規化は今の時代でも必要ですか?
A.必要です。NoSQL や分析用テーブルでは意図的に崩すこともありますが、判断の起点となる「正規化された姿」を知っていることが前提になります。

関連トピック

もっと学びたい方へ(おすすめ書籍)

本セクションはAmazonアソシエイトのリンクを含みます。

オンライン個別指導

もっと深くDBを学びたい方へ。

たいてっくが、SQL・データベース設計・パフォーマンスチューニング・ IPAデータベーススペシャリスト対策まで、1対1で学習をサポートします。まずは無料相談から。

無料相談を予約する →