リレーショナルDBの基本概念を図解で理解する

リレーショナルDBの基本概念を図解で理解する

はじめに

Webアプリケーションやバックエンドの学習を進めると、必ずと言っていいほど「リレーショナルデータベース(RDB)」に出会います。MySQL、PostgreSQL、SQLite、Oracle、SQL Server などはすべて RDB の仲間です。

RDB は「データを表(テーブル)として持ち、表と表を関連(リレーション)でつなぐ」という一貫した考え方の上に成り立っています。本記事では、テーブル・主キー・外部キー・正規化・結合・トランザクション・インデックスといった基本概念を、図で整理しながら入門者向けに解説します。

そもそもリレーショナルとは何か

RDB の「リレーショナル(関係)」は、数学の集合と関係の理論に由来しますが、実務でまず押さえるべきは次のシンプルなイメージで十分です。

- データは行(レコード)と列(カラム)を持つ表(テーブル)で管理する
- 表と表は共通の値を介してつながる

まずは1枚のテーブルを見てみましょう。

```text
users テーブル
+----+----------+---------------------+
| id | name | email |
+----+----------+---------------------+
| 1 | 田中 | tanaka@example.com |
| 2 | 佐藤 | sato@example.com |
| 3 | 鈴木 | suzuki@example.com |
+----+----------+---------------------+
```

- カラム(列): `id` `name` `email` のような属性。列ごとに型が決まる
- レコード(行): 「1人分のデータ」のような意味あるひとまとまり
- テーブル: 同じ構造のレコードを集めたもの

主キー(Primary Key)— 行を一意に識別する

テーブルの中で「その1行を確実に特定できる列」を主キーと呼びます。上の例では `id` が主キーです。主キーは一意(重複しない)でNOT NULL(空にできない)という性質を持ちます。

名前やメールは変わりうるし重複もしうるため、変わらない・重複しない番号を1本用意して「この行はこれ」と指し示せるようにします。実務では連番の `id`(サロゲートキー)を主キーにすることが多いです。

外部キー(Foreign Key)— テーブルをつなぐ

RDB の真価は、複数のテーブルを関連づけられる点にあります。その仕組みが外部キーです。

```text
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種類です。 ```text 【1対1】 users 1 ── 1 user_profiles 【1対多】 users 1 ──< orders (最も多い) 【多対多】 students >──< courses ``` 多対多はテーブルを直接つなげないため、中間テーブル(結合テーブル)を挟みます。 ```text students enrollments courses +----+------+ +------------+---------+ +----+------+ | id | name | | student_id | course_id| | id | title| +----+------+ +------------+---------+ +----+------+ | 1 | 田中 |<── | 1 | 100 |─>| 100| 数学 |
| 2 | 佐藤 |<── | 1 | 200 |─>| 200| 英語 |
+----+------+ | 2 | 100 | +----+------+
+------------+---------+
↑ 中間テーブル
```

「多対多を見たら中間テーブル」と覚えておくと設計で迷いません。

正規化 — 重複をなくして矛盾を防ぐ

初心者がやりがちなのが、1枚の巨大なテーブルに何でも詰め込む設計です。

```text
悪い例: すべて1枚に
+----+----------+---------------------+--------+
| id | user_name| user_email | amount |
+----+----------+---------------------+--------+
| 10 | 田中 | tanaka@example.com | 3000 |
| 11 | 田中 | tanaka@example.com | 1500 | ← 重複
| 12 | 佐藤 | sato@example.com | 8000 |
+----+----------+---------------------+--------+
```

この設計は更新異常(メール変更時に全行を直す必要がある)、無駄な重複、挿入異常(注文の無いユーザーを登録できない)を招きます。

解決するのが正規化です。「1つの事実は1か所にだけ持つ」ように表を分割します。

```text
users orders
+----+--------+----------+ +----+---------+--------+
| id | name | email | | id | user_id | amount |
+----+--------+----------+ +----+---------+--------+
| 1 | 田中 | tanaka.. | | 10 | 1 | 3000 |
| 2 | 佐藤 | sato.. | | 11 | 1 | 1500 |
+----+--------+----------+ | 12 | 2 | 8000 |
+----+---------+--------+
```

入門者はまず「同じ事実を複数箇所に持たない」という原則を意識すれば十分です。性能のためにあえて重複を許す「非正規化」もありますが、まず正しく正規化できることが前提です。

テーブルを定義してみる(DDL)

```sql
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 で再びつなげて取り出します。

```sql
SELECT users.name, orders.amount
FROM orders
JOIN users ON orders.user_id = users.id
WHERE users.name = '田中';
```

JOIN の種類は入門段階では次の2つで十分です。

```text
INNER JOIN : 両方に対応する行があるものだけ返す
LEFT JOIN : 左側(users)を全部残す(注文が無い人も NULL で返る)
```

「注文したことがない人も含めたい」なら `LEFT JOIN`、というように目的で使い分けます。

トランザクション —「全部成功か、全部やり直し」

複数の更新を「ひとまとまり」として扱う仕組みがトランザクションです。送金が典型例です。

```sql
BEGIN;
UPDATE accounts SET balance = balance - 1000 WHERE id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE id = 2;
COMMIT; -- 途中で失敗すれば ROLLBACK; で全部なかったことにする
```

片方だけ成功してお金が消える、といった中途半端な状態を防ぎます。この信頼性をまとめて ACID(原子性・一貫性・独立性・永続性)と呼び、RDB が業務システムで選ばれ続ける大きな理由になっています。

インデックス — 検索を速くする索引

行数が増えると条件検索が遅くなります。全行を上から調べる(フルスキャン)代わりに、本の巻末索引のような早見表を持つのがインデックスです。

```sql
CREATE INDEX idx_users_email ON users(email);
```

ただし検索は速くなる一方、書き込みは索引更新分だけ遅くなり容量も増えます。よく検索する列に絞って付けるのが基本です。

全体像のまとめ図

```text
テーブル(行 × 列)
├─ 主キー : 行を一意に識別
├─ 外部キー : 他テーブルを参照しつなぐ
└─ インデックス: 検索を高速化する索引
リレーション(1対1 / 1対多 / 多対多 → 中間テーブル)
正規化 : 重複を排除し矛盾を防ぐ
JOIN : 分けた表をつないで取り出す
トランザクション : 一連の更新をまとめて保証(ACID)
```

注意点

- 主キーは必ず設ける
- 外部キー制約は積極的に使い、データ破損を DB に防いでもらう
- 正規化しすぎ・しなさすぎの両方に注意(まず正規化、性能問題は実測後に非正規化を検討)
- インデックスは書き込みコストとのトレードオフを意識する
- 型名や文法は製品ごとに異なるため、使う製品の公式ドキュメントも確認する

まとめ

- RDB はデータをテーブルで持ち、主キーで行を識別し、外部キーでつなぐ
- 関係は 1対多が基本、多対多は中間テーブルで表現する
- 正規化で「1つの事実は1か所」を保ち、JOINで必要な形に組み立てる
- トランザクション(ACID)が信頼できる更新を保証し、インデックスが検索を速くする

これらは製品を問わず共通の土台です。まずは SQLite で小さな表を作り、分けて・つないで、を手で試すのがおすすめです。

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください