データベース設計の基本!第1正規形・第2正規形・第3正規形を初心者向けに徹底解説
生徒
「データベースの設計で『正規化』っていう言葉が出てきたんですけど、難しそうで不安です…。」
先生
「難しく考える必要はありませんよ。正規化というのは、一言で言うと『データの整理整頓』のことです。散らかった机の上を片付けるように、表を使いやすく分ける作業なんです。」
生徒
「バラバラにするのに、使いやすくなるんですか?」
先生
「はい。同じ情報を何度も書かなくて済むようになったり、データが矛盾するのを防いだりできるんです。パソコン初心者の方でもわかるように、注文書を例にして一緒に見ていきましょう。」
1. SQLとは何か?
SQLは、データベースと呼ばれる「大量のデータを整理して保存する箱」に対して指示を出すための言語です。例えば、会員名簿の中から特定の人を探したり、新しい人を追加したりするときに使います。銀行の残高確認や、ショッピングサイトの在庫管理など、私たちの身の回りのあらゆるシステムで、このSQLという言葉が使われています。データベースは巨大な「表」の集まりであり、その表を人間が操作しやすいように整えるルールが、今回学習する「正規化」なのです。
2. データベース設計のキモ「正規化」とは?
データベースを設計するときに、最も大切だと言われるのが「正規化(せいきか)」です。これは、データを管理しやすくするために、表の構造をルールに従って整える作業を指します。もし正規化を行わずに、一つの大きな表にすべての情報を詰め込んでしまうと、後からデータを書き換えたいときに「あっちの行は直したけど、こっちの行を直すのを忘れた!」といったミスが起こりやすくなります。これを「データの矛盾」と呼びます。
正規化にはいくつかの段階がありますが、まずは基本となる「第1正規形」「第2正規形」「第3正規形」の3つを理解することが、データベースエンジニアへの第一歩です。初心者の方でもイメージしやすいように、ネットショップの「注文伝票」を例にして考えてみましょう。
3. 第1正規形:繰り返しの項目をなくそう
第1正規形のルールはとてもシンプルです。「一つのマス目(セル)には、一つのデータだけを入れる」という決まりです。例えば、一回の注文で「りんご」と「バナナ」を買ったとき、一つの枠の中に「りんご, バナナ」と無理やり詰め込んではいけません。これを「繰り返しの排除」と言います。
以下の表は、正規化されていない「ダメな表」の例です。注文商品名や単価が一つの枠にまとまってしまっていますね。
注文ID | 顧客名 | 注文商品名 | 単価
-------+----------+------------------+-----------
101 | 山田太郎 | りんご, バナナ | 100, 150
102 | 佐藤花子 | みかん | 80
103 | 鈴木一郎 | りんご, みかん | 100, 80
これでは、後から「りんごが何個売れたか」を集計するのがとても大変になります。そこで、各データを一行ずつにバラバラにします。これが「第1正規形」です。
注文ID | 顧客名 | 注文商品名 | 単価
-------+----------+------------+------
101 | 山田太郎 | りんご | 100
101 | 山田太郎 | バナナ | 150
102 | 佐藤花子 | みかん | 80
103 | 鈴木一郎 | りんご | 100
103 | 鈴木一郎 | みかん | 80
このように整理すると、SQLを使って特定の商品の売り上げを簡単に計算できるようになります。実際にSQLを使って、商品ごとの売り上げ件数を確認する命令を書いてみましょう。
SELECT 注文商品名, COUNT(*) AS 注文数
FROM 注文テーブル
GROUP BY 注文商品名;
注文商品名 | 注文数
-----------+-------
りんご | 2
バナナ | 1
みかん | 2
4. 第2正規形:一部分だけで決まる項目を切り出す
第1正規形で表はきれいになりましたが、まだ問題があります。「山田太郎」という名前や「りんご」の「単価100円」という情報が、何度も繰り返し登場していますね。第2正規形では、「主キー(その行を特定するための特別な番号)」の一部分だけに依存している項目を、別の表に切り出します。
今回の例では、「注文ID」と「注文商品名」のセットが決まれば、その行が特定できます。しかし、「単価」は「注文商品名」さえわかれば決まる情報です。このように「セットの一部にだけ関係があるデータ」を別の表に分けるのがポイントです。これにより、「商品マスター」という表を作ることができます。
【注文テーブル(第2正規形)】
注文ID | 顧客名 | 注文商品名
-------+----------+------------
101 | 山田太郎 | りんご
101 | 山田太郎 | バナナ
102 | 佐藤花子 | みかん
103 | 鈴木一郎 | りんご
103 | 鈴木一郎 | みかん
【商品マスター】
商品名 | 単価
---------+------
りんご | 100
バナナ | 150
みかん | 80
このように分けることで、もし「りんご」の値上げが決まっても、商品マスターの一箇所を書き換えるだけで済むようになります。SQLでデータを更新する例を見てみましょう。
UPDATE 商品マスター
SET 単価 = 120
WHERE 商品名 = 'りんご';
商品名 | 単価
---------+------
りんご | 120
バナナ | 150
みかん | 80
5. 第3正規形:連鎖して決まる項目を切り出す
最後に、第3正規形です。これは「主キー以外の項目同士で決まっている関係」を解消することです。先ほどの「注文テーブル」をよく見てください。ここでは「注文ID」が決まると「顧客名」が決まるという関係があります。しかし、「顧客名」は「注文ID」という主キーに直接結びついているというよりは、本来は「顧客ID」のようなものに紐付くべき情報です。
「Aが決まればBが決まり、Bが決まればCが決まる」という連鎖(推移的関数従属といいます)を断ち切ります。顧客の情報は「顧客マスター」として独立させましょう。実務では「顧客ID」という数字を使って管理するのが一般的です。
【注文テーブル(第3正規形)】
注文ID | 顧客ID | 商品名
-------+--------+------------
101 | C01 | りんご
101 | C01 | バナナ
102 | C02 | みかん
103 | C03 | りんご
103 | C03 | みかん
【顧客マスター】
顧客ID | 顧客名
-------+----------
C01 | 山田太郎
C02 | 佐藤花子
C03 | 鈴木一郎
これで、一つの事実は一箇所に保存されるという理想的な形になりました。バラバラにした表を組み合わせて情報を表示するには、SQLの「JOIN(結合)」という命令を使います。
SELECT 注文.注文ID, 顧客.顧客名, 注文.商品名
FROM 注文テーブル AS 注文
JOIN 顧客マスター AS 顧客 ON 注文.顧客ID = 顧客.顧客ID;
注文ID | 顧客名 | 商品名
-------+----------+------------
101 | 山田太郎 | りんご
101 | 山田太郎 | バナナ
102 | 佐藤花子 | みかん
103 | 鈴木一郎 | りんご
103 | 鈴木一郎 | みかん
6. なぜ正規化が必要なのか?メリットを整理
ここまで、少し手間をかけて表を分けてきました。なぜこんなに細かく分ける必要があるのでしょうか?それは、将来のトラブルを防ぐためです。もし表を分けていないと、以下のような困ったことが起こります。
- データの重複:同じ顧客の名前が何百回も出てくると、データの保存容量を無駄に使ってしまいます。
- 更新の漏れ:住所変更があったとき、100箇所のデータを書き換えるのは大変ですし、一つでも忘れるとデータがバラバラになってしまいます。
- 追加の不便さ:まだ何も買っていない新しいお客様の情報を登録したいとき、注文テーブルしかないと「注文していないのに注文情報を入れる」というおかしな状況になります。
正規化をしっかりと行うことで、データの正確性(整合性)を保ち、長く使い続けられるシステムを作ることができるのです。最初は「表が増えてややこしい」と感じるかもしれませんが、慣れてくるとこの整理整頓された状態がとても心地よく、プログラムも書きやすくなることに気づくはずです。
データベース設計は、いわば建物の設計図を書くような作業です。土台がしっかりしていれば、その上に建つアプリケーションも安定します。第1、第2、第3正規形という階段を一段ずつ登って、美しいデータベース設計を目指しましょう。