コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
第1正規形
第 1 正規形
このレッスンで分かること
- 1NF の定義「全ての属性が原子値」の本当の意味
- JSON や配列をセルに入れる設計の是非
- 1NF 違反を直す 2 通りのアプローチ
第1正規形 とは
繰り返し項目を排除し原子値だけで構成する。本レッスンでは、第1正規形 の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
1NF の定義
第 1 正規形 (1NF) とは、テーブルの全カラムが 原子値 (Atomic Value) だけを持つ状態を指します。原子値とは「それ以上分解する必要のない単一の値」のことです。
逆に言えば、1 つのセルに「複数の値が入っている」「内部構造を持つ」状態は 1NF 違反です。具体的には次のような設計が問題視されます。
| user_id | name | phones |
|---|---|---|
| 1 | Alice | 090-1111-1111, 080-2222-2222 |
| 2 | Bob | 070-3333-3333 |
phones セルに 2 つの電話番号がカンマ区切りで入っています。これは 1NF 違反です。
なぜ 1NF 違反がダメか
「カンマで区切ればいいじゃん」と思うかもしれません。実際これで動くこともあります。しかし、次の操作が悲惨になります。
- 「080 で始まる番号を持つユーザーを検索」 —
LIKE '%080-%'に頼るしかなく、インデックスが効かない - 「2 番目の番号だけ更新」 —
SUBSTRINGで文字列処理。バグの温床 - 集計 — 「番号を 1 つだけ持つユーザー」を数えるのに、
COMMAの出現回数を数える羽目に - 整合性 — 重複番号やフォーマット違反を防げない
SQL クエリ
SELECT * FROM users WHERE phones LIKE '%080-%';このクエリは全件走査確定です。インデックスを貼っても、LIKE '%...%' だと使えません。
1NF に直す 2 つのアプローチ
案 A:別テーブルに切り出す(推奨)
多値属性は別テーブルにします。
SQL クエリ
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50)
);
CREATE TABLE user_phones (
user_id INT,
phone VARCHAR(20),
PRIMARY KEY (user_id, phone),
FOREIGN KEY (user_id) REFERENCES users(id)
);これで SELECT user_id FROM user_phones WHERE phone LIKE '080-%' がインデックスで動きます。
案 B:行を増やす(非推奨)
users テーブルにそのまま行を増やす方法もあります。
| user_id | name | phone |
|---|---|---|
| 1 | Alice | 090-1111-1111 |
| 1 | Alice | 080-2222-2222 |
name が冗長になり、Alice の名前を変えるとき複数行更新が必要です。これは 2NF 違反に進むので、案 A を選びましょう。
JSON カラムは 1NF 違反か
最近の DB は JSON 型をネイティブサポートしています。これは 1NF 違反でしょうか。
答えは「目的次第」です。
- 検索・JOIN・集計の対象 にするなら 1NF 違反扱い。別テーブルに切り出す
- 不定形の付随情報 (ログのメタデータ、設定スナップショット、API レスポンスのキャッシュなど)なら
JSONのままで OK
PostgreSQL の JSONB や MySQL の JSON は GIN インデックスや関数インデックスで部分検索もできますが、本格的に検索したいなら正規テーブルが速いです。
複合属性も 1NF 違反
address が "東京都渋谷区..." のような 1 文字列で入っている場合、これも厳密には 1NF 違反です。市区町村だけで集計したいなら分割します。
SQL クエリ
CREATE TABLE addresses (
user_id INT PRIMARY KEY,
zip CHAR(7),
prefecture VARCHAR(10),
city VARCHAR(20),
street VARCHAR(50)
);ただし「住所は表示するだけ」「全文検索する」なら 1 カラムで十分なこともあります。
トレードオフと現実解
理論を厳格に適用すると、すべてのカラムを最小単位に分解することになります。実務では「検索・更新・集計の単位」を基準に粒度を決めます。例えば日付は year、month、day に分けず DATE 1 つで持ち、必要なら YEAR(d) 関数で抽出します。
「セルに何か区切り文字が入っていたら 1NF 違反を疑え」が実務の合言葉。
やってみよう
- 自分のプロジェクトのテーブルから「カンマ区切り」「JSON」「複合文字列」になっている列を 3 つ探す
- それぞれ「どんな検索が遅くなるか」を 1 文で書き出す
- 別テーブルに切り出した場合の
CREATE TABLEを書く
よくある質問
Q. なぜ正規化するのですか?
A. データの重複を排除し、更新時の不整合(更新異常・挿入異常・削除異常)を防ぐためです。第 3 正規形まで適用すると保守性が大きく上がります。一方で JOIN が増えるため、検索性能と保守性のバランスで非正規化することもあります。
Q. 1NF と 2NF の違いは?
A. 1NF は「各セルが単一値」、2NF は「主キーの一部だけに依存するカラムをテーブル分割」する形です。複合キーの片方だけに紐づく属性は別テーブルに切り出すと 2NF になります。1NF を満たさないと 2NF 以降の議論はできません。
Q. 全部正規化すれば最適ですか?
A. 理想ですが、JOIN が増えてパフォーマンスが落ちる場面では非正規化(カラム複製、計算結果のキャッシュ)も有効です。OLTP は 3NF、レポーティング用 DWH はスタースキーマ(非正規化)と、用途で使い分けるのが現代的なアプローチです。
次のレッスン
次は 第2正規形 で、複合主キーの一部にしか依存しない列(部分関数従属)を別テーブルに切り出す方法を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- 第1正規形 の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. 第1正規形 とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン