リレーショナルデータベース

リレーショナルデータベースとは

リレーショナルデータベース(RDB / RDBMS)は、データを行と列からなる表(テーブル)で管理するデータベースです。PostgreSQL、MySQL、SQLite などが代表例で、操作には [[SQL]] を使います。

users テーブル
| id | name     | email             |
|----|----------|-------------------|
| 1  | 山田太郎 | yamada@example.com |
| 2  | 佐藤花子 | sato@example.com   |

初学者向けポイント

  • テーブル同士を [[主キーと外部キー]] で関連づけ、[[テーブル結合(JOIN)]] で組み合わせて使う
  • 正規化 = データの重複をなくすようテーブルを分割する設計手法
  • ACID特性(原子性・一貫性・独立性・永続性)により、お金や在庫のような「絶対に壊れてはいけない」データに強い

表計算ソフトとの違い

行と列という見た目は Excel によく似ていますが、RDB が引き受けている仕事は違います。

  • 形を先に決める — どの列にどんな種類の値が入るかを定義し、それに反する値は保存させない
  • 1種類のものを1テーブルに置く — 「顧客名入りの注文一覧」は保存せず、取り出すときに [[テーブル結合(JOIN)]] で組み立てる
  • 同時に書き換えても壊れない — 在庫数 10 を2人が同時に1ずつ減らすと、表計算では後から保存したほうが上書きして 9 になりますが、RDB は [[トランザクションとACID]] と [[ロックとデッドロック]] の仕組みで 8 にたどり着けます

スキーマと制約 — 先に形を決める

テーブルの定義(スキーマ)では、列ごとにデータ型と制約を決めます。ここで手を抜くと、後から「入っているはずのない値」が現実に入ります。

  • データ型は列にできることを決めます。日付を文字列で持つと '2026-1-5''2026-01-05' が混ざって並べ替えが狂い、金額を浮動小数点数(FLOAT)にすると 0.1 + 0.20.30000000000000004 になって合計がずれます。金額は DECIMAL / NUMERIC を使います
  • 制約はDBに守らせるルールで、NOT NULL(空を許さない)・UNIQUE(重複を許さない)・CHECK(条件を満たす値だけ)がよく使われます
  • 入力チェックはアプリ側にもありますが、管理画面・バッチ処理・手動SQLとデータが入る経路は複数あります。そのすべてに効くのはDBの制約だけです

主キーと外部キーの設計は [[主キーと外部キー]] で扱います。

NULL でつまずかない

NULL は「値が入っていない」ことを表す特別な印で、0 でも空文字でもありません。

  • WHERE status != 'done' は status が NULL の行を拾いません。NULL との比較は真にも偽にもならない(不明)ためです
  • 判定は IS NULL / IS NOT NULL で行います。= NULL は常に不明になり1行も返りません
  • COUNT(列名) は NULL を数えず、COUNT(*) の結果と食い違います

対策は単純で、「値が無い」を表す必要のない列には最初から NOT NULL を付けることです。

代表的な製品

製品特徴
[[PostgreSQL]]機能が豊富でJSON型なども扱える。OSSの定番
[[MySQL]]Webサービスで広く採用。レプリケーション実績が豊富
[[SQLite]]サーバー不要のファイル型。組み込み・小規模用途

次に読むための地図

RDB の周辺は覚えることが多く、どこから手を付けるか迷いがちです。「いま何をしたいか」で束ねると選びやすくなります。上から順に読む必要はありません。

設計する — データの形を決める

最初にやることです。[[ER図とデータモデリング]] で登場人物と関係を図にし、[[正規化]] で「1つの事実は1か所だけ」に整理し、[[主キーと外部キー]] で行の識別と参照のルールを決めます。この3つはセットで動きます。

取り出す・書き込む

基本の道具は [[SQL]] で、複数テーブルにまたがるデータは [[テーブル結合(JOIN)]] で組み立てます。アプリのコードからは [[ORM]] 経由で扱うことが多く、その場合も裏で発行される SQL を読めることが前提になります。

速くする

遅くなってから手を打つ領域です。まず [[実行計画とクエリチューニング]] で原因を測り、多くはそこから [[データベースインデックス]] で解決します。足りなければ [[コネクションプーリング]]・[[キャッシュ戦略]]・[[シャーディングとパーティショニング]] と手段が広がります。文章の中身を探すなら [[全文検索]] です。

壊さない — 同時アクセスに耐える

土台は [[トランザクションとACID]]。「どこまで他人の途中経過が見えてよいか」を決めるのが [[トランザクション分離レベル]]、その裏で順番待ちを作っているのが [[ロックとデッドロック]] です。落ちても確定済みのデータを失わない仕組みは [[トランザクションログとWAL]] が支えます。

運用する — 変え続ける・失わない・守る

テーブル定義の変更は [[スキーママイグレーション]] としてコード化し、[[バックアップとリカバリ]] で戻し方を用意し、[[レプリケーション]] で複製を持ちます。権限は [[データベースのユーザーと権限管理]] で絞り、入力からクエリを組む前に [[SQLインジェクション]] を理解しておきます。

RDB の外へ — 向かない仕事を見分ける

苦手な領域は別の道具が担当します。柔軟なスキーマと水平分散なら [[NoSQL]](代表格が [[MongoDB]])、使い捨ての一時データなら [[Redis]]、関係を何段もたどる探索なら [[グラフデータベース]]。大量集計は [[OLTPとOLAP]] を押さえた上で [[データパイプライン・ETL]] で [[データウェアハウス]] へ写すのが定石です。

関連技術とのつながり

  • [[SQL]] — RDBを操作する言語
  • [[NoSQL]] — RDBが苦手な領域(超大量データ・柔軟なスキーマ)を補う選択肢
  • [[Docker]] — 開発環境でのDB起動はコンテナが定番
  • [[クラウドコンピューティング]] — マネージドDBサービスとしてバックアップや複製の運用を任せられる。テーブル設計やクエリの改善は利用者の責任として残る
  • [[トランザクションログとWAL]] — ACID特性の永続性を内部で支える仕組み
  • [[データベースのユーザーと権限管理]] — 接続に使うDBユーザーと操作範囲の設計
  • [[ミドルウェア]] — サーバー上では、Webサーバーなどと並ぶミドルウェアの一種として運用される
Q: 列の値が NULL かどうかを判定する書き方はどれ?
- [ ] `WHERE status = NULL`
- [x] `WHERE status IS NULL`
- [ ] `WHERE status != 'done'`
解説: NULL との比較は真にも偽にもならないため `= NULL` では1行も返りません。判定には `IS NULL` / `IS NOT NULL` を使います。`!= 'done'` も NULL の行を拾いません。

Q: 金額を格納する列のデータ型として記事が勧めているのはどれ?
- [ ] 浮動小数点数(FLOAT)
- [x] DECIMAL / NUMERIC
- [ ] 日付を含む文字列
解説: FLOAT では `0.1 + 0.2` が `0.30000000000000004` になるように誤差が出て、合計がずれます。金額には DECIMAL / NUMERIC を使います。

Q: クエリが遅いとき、記事が最初の一歩として挙げているのはどれ?
- [ ] とりあえず全部の列にインデックスを貼る
- [ ] キャッシュを追加して結果を再利用する
- [x] 実行計画を見て、どこが遅いのかを測る
解説: 「速くする」はまず実行計画で原因を測るところから始め、多くはそこからインデックスで解決します。接続の使い回しやキャッシュ、データの分割はその先の手段です。