リレーショナルDBの基本概念を図解で理解する
はじめに
Webアプリケーションやバックエンドの学習を進めると、必ずと言っていいほど「リレーショナルデータベース(RDB)」に出会います。MySQL、PostgreSQL、SQLite、Oracle、SQL Server などはすべて RDB の仲間です。
RDB は「データを表(テーブル)として持ち、表と表を関連(リレーション)でつなぐ」という一貫した考え方の上に成り立っています。本記事では、テーブル・主キー・外部キー・正規化・結合・トランザクション・インデックスといった基本概念を、図で整理しながら入門者向けに解説します。
そもそもリレーショナルとは何か
RDB の「リレーショナル(関係)」は、数学の集合と関係の理論に由来しますが、実務でまず押さえるべきは次のシンプルなイメージで十分です。
- データは行(レコード)と列(カラム)を持つ表(テーブル)で管理する
- 表と表は共通の値を介してつながる
まずは1枚のテーブルを見てみましょう。
users テーブル +----+----------+---------------------+ | id | name | email | +----+----------+---------------------+ | 1 | 田中 | tanaka@example.com | | 2 | 佐藤 | sato@example.com | | 3 | 鈴木 | suzuki@example.com | +----+----------+---------------------+
- カラム(列):
idnameemailのような属性。列ごとに型が決まる - レコード(行): 「1人分のデータ」のような意味あるひとまとまり
- テーブル: 同じ構造のレコードを集めたもの
主キー(Primary Key)— 行を一意に識別する
テーブルの中で「その1行を確実に特定できる列」を主キーと呼びます。上の例では id が主キーです。主キーは一意(重複しない)でNOT NULL(空にできない)という性質を持ちます。
名前やメールは変わりうるし重複もしうるため、変わらない・重複しない番号を1本用意して「この行はこれ」と指し示せるようにします。実務では連番の id(サロゲートキー)を主キーにすることが多いです。
外部キー(Foreign Key)— テーブルをつなぐ
RDB の真価は、複数のテーブルを関連づけられる点にあります。その仕組みが外部キーです。
users orders +----+--------+ +----+---------+--------+ | id | name | | id | user_id | amount | +----+--------+ +----+---------+--------+ | 1 | 田中 | <──────┐ | 10 | 1 | 3000 | | 2 | 佐藤 | <──┐ └───────| 11 | 1 | 1500 | | 3 | 鈴木 | └───────────| 12 | 2 | 8000 | +----+--------+ +----+---------+--------+ ↑ 主キー ↑ 外部キー(users.id を参照)
orders.user_id は users.id を指す外部キーです。これにより「どの注文が誰のものか」を辿れます。さらに外部キーには参照整合性の役割があり、存在しないユーザーの注文を作れないよう DB 側で防げます。
リレーションの種類 — 1対多と多対多
テーブル間の関係は主に3種類です。
【1対1】 users 1 ── 1 user_profiles 【1対多】 users 1 ──< orders (最も多い) 【多対多】 students >──< courses
多対多はテーブルを直接つなげないため、中間テーブル(結合テーブル)を挟みます。
students enrollments courses
+----+------+ +------------+---------+ +----+------+
| id | name | | student_id | course_id| | id | title|
+----+------+ +------------+---------+ +----+------+
| 1 | 田中 |<── | 1 | 100 |─>| 100| 数学 |
| 2 | 佐藤 |<── | 1 | 200 |─>| 200| 英語 |
+----+------+ | 2 | 100 | +----+------+
+------------+---------+
↑ 中間テーブル
「多対多を見たら中間テーブル」と覚えておくと設計で迷いません。
正規化 — 重複をなくして矛盾を防ぐ
初心者がやりがちなのが、1枚の巨大なテーブルに何でも詰め込む設計です。
悪い例: すべて1枚に +----+----------+---------------------+--------+ | id | user_name| user_email | amount | +----+----------+---------------------+--------+ | 10 | 田中 | tanaka@example.com | 3000 | | 11 | 田中 | tanaka@example.com | 1500 | ← 重複 | 12 | 佐藤 | sato@example.com | 8000 | +----+----------+---------------------+--------+
この設計は更新異常(メール変更時に全行を直す必要がある)、無駄な重複、挿入異常(注文の無いユーザーを登録できない)を招きます。
解決するのが正規化です。「1つの事実は1か所にだけ持つ」ように表を分割します。
users orders
+----+--------+----------+ +----+---------+--------+
| id | name | email | | id | user_id | amount |
+----+--------+----------+ +----+---------+--------+
| 1 | 田中 | tanaka.. | | 10 | 1 | 3000 |
| 2 | 佐藤 | sato.. | | 11 | 1 | 1500 |
+----+--------+----------+ | 12 | 2 | 8000 |
+----+---------+--------+
入門者はまず「同じ事実を複数箇所に持たない」という原則を意識すれば十分です。性能のためにあえて重複を許す「非正規化」もありますが、まず正しく正規化できることが前提です。
テーブルを定義してみる(DDL)
CREATE TABLE users ( id INTEGER PRIMARY KEY, name VARCHAR(50) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE ); CREATE TABLE orders ( id INTEGER PRIMARY KEY, user_id INTEGER NOT NULL, amount INTEGER NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) );
FOREIGN KEY ... REFERENCES により、参照整合性が DB レベルで守られます。
結合(JOIN)— つながりを使って取り出す
分割したテーブルは、JOIN で再びつなげて取り出します。
SELECT users.name, orders.amount FROM orders JOIN users ON orders.user_id = users.id WHERE users.name = '田中';
JOIN の種類は入門段階では次の2つで十分です。
INNER JOIN : 両方に対応する行があるものだけ返す LEFT JOIN : 左側(users)を全部残す(注文が無い人も NULL で返る)
「注文したことがない人も含めたい」なら LEFT JOIN、というように目的で使い分けます。
トランザクション —「全部成功か、全部やり直し」
複数の更新を「ひとまとまり」として扱う仕組みがトランザクションです。送金が典型例です。
BEGIN; UPDATE accounts SET balance = balance - 1000 WHERE id = 1; UPDATE accounts SET balance = balance + 1000 WHERE id = 2; COMMIT; -- 途中で失敗すれば ROLLBACK; で全部なかったことにする
片方だけ成功してお金が消える、といった中途半端な状態を防ぎます。この信頼性をまとめて ACID(原子性・一貫性・独立性・永続性)と呼び、RDB が業務システムで選ばれ続ける大きな理由になっています。
インデックス — 検索を速くする索引
行数が増えると条件検索が遅くなります。全行を上から調べる(フルスキャン)代わりに、本の巻末索引のような早見表を持つのがインデックスです。
CREATE INDEX idx_users_email ON users(email);
ただし検索は速くなる一方、書き込みは索引更新分だけ遅くなり容量も増えます。よく検索する列に絞って付けるのが基本です。
全体像のまとめ図
テーブル(行 × 列) ├─ 主キー : 行を一意に識別 ├─ 外部キー : 他テーブルを参照しつなぐ └─ インデックス: 検索を高速化する索引 リレーション(1対1 / 1対多 / 多対多 → 中間テーブル) 正規化 : 重複を排除し矛盾を防ぐ JOIN : 分けた表をつないで取り出す トランザクション : 一連の更新をまとめて保証(ACID)
注意点
- 主キーは必ず設ける
- 外部キー制約は積極的に使い、データ破損を DB に防いでもらう
- 正規化しすぎ・しなさすぎの両方に注意(まず正規化、性能問題は実測後に非正規化を検討)
- インデックスは書き込みコストとのトレードオフを意識する
- 型名や文法は製品ごとに異なるため、使う製品の公式ドキュメントも確認する
まとめ
- RDB はデータをテーブルで持ち、主キーで行を識別し、外部キーでつなぐ
- 関係は 1対多が基本、多対多は中間テーブルで表現する
- 正規化で「1つの事実は1か所」を保ち、JOINで必要な形に組み立てる
- トランザクション(ACID)が信頼できる更新を保証し、インデックスが検索を速くする
これらは製品を問わず共通の土台です。まずは SQLite で小さな表を作り、分けて・つないで、を手で試すのがおすすめです。
