第7章 基本用語と命名規則 — テーブル・カラム・主キー・外部キー
この章で学ぶこと
- テーブル / カラム / レコード / 主キー / 外部キーの違いを自分の言葉で説明できる
- グローバル標準に通用する命名規則 (
snake_case/ 複数形 /is_接頭辞など) に従ってテーブル定義を書ける - スキーマ / インデックス / ビューといった周辺用語の役割を理解する
第 4 章 CRUD と ACID と 第 5 章 リレーショナルDB 入門 を理解していること。
基本用語の全体像
リレーショナルデータベースの構成要素を階層で見ると、次のようになります。
具体的なイメージで見てみましょう。
データベース: company_db
└── スキーマ: public
├── テーブル: employees
│ ├── カラム: id (INTEGER, PRIMARY KEY)
│ ├── カラム: name (VARCHAR(100), NOT NULL)
│ ├── カラム: email (VARCHAR(255), UNIQUE)
│ └── カラム: department_id (INTEGER, FOREIGN KEY)
│
└── テーブル: departments
├── カラム: id (INTEGER, PRIMARY KEY)
└── カラム: name (VARCHAR(50))
スキーマ (schema): テーブルやインデックスをまとめる「名前空間」。1 つのデータベースの中に複数のスキーマを作って、テーブルを論理的に分けられる。PostgreSQL ではデフォルトで public スキーマがある。MySQL では「スキーマ」と「データベース」がほぼ同義 (微妙に違う) で、初学者が混乱しやすいので注意。
テーブルの構造
テーブルの構造を視覚的に見てみましょう。
employees テーブル
+----+----------+--------------------+---------------+
| id | name | email | department_id |
| (PK)| | (UNIQUE) | (FK) |
+----+----------+--------------------+---------------+
| 1 | 山田 太郎 | yamada@example.com | 10 | ← レコード (行)
| 2 | 鈴木 花子 | suzuki@example.com | 20 |
| 3 | 佐藤 次郎 | sato@example.com | 10 |
+----+----------+--------------------+---------------+
^ ^
| |
カラム (列) のヘッダー セル (個々の値)
| 概念 | 何か |
|---|---|
| テーブル | データを格納する 2 次元の表 |
| カラム (列) | 縦方向の項目 (例: id / name / email) |
| レコード (行) | 横方向の 1 件分のデータ |
| セル | 行と列の交差点にある個々の値 |
用語の正式名称と別名
データベースの用語は、文脈や教科書によって複数の呼び方があります。本ガイドではこの一覧の「正式名称」を主に使いますが、他の呼び方も知っておくと混乱しません。
| 正式名称 | よく使われる別名 | 英語 | 何を指すか |
|---|---|---|---|
| テーブル | 表 / 関係 | Table / Relation | データを格納する基本単位 |
| カラム | 列 / フィールド / 属性 | Column / Field / Attribute | データの項目 |
| レコード | 行 / タプル / ロウ | Record / Row / Tuple | 1 件分のデータ |
| 主キー | プライマリキー / PK | Primary Key | レコードを一意に識別する値 |
| 外部キー | フォーリンキー / FK | Foreign Key | 他テーブルを参照する値 |
| スキーマ | (同名) | Schema | テーブルなどをまとめる名前空間 |
| インデックス | 索引 | Index | 検索を高速化するためのデータ構造 |
| ビュー | 仮想表 | View | 既存テーブルから派生する「見え方」 |
主キーと外部キーの詳細は第 10 章 キー概念で扱います。ここではざっくりと:
- 主キー (PK): そのテーブルの中で「この行はこれ」と一意に決まる値 (例:
users.id) - 外部キー (FK): 別のテーブルの主キーを指す値 (例:
orders.user_id→users.id)
外部キーがあることで「この注文は誰の注文か」が表同士の関係でたどれます。
命名規則 — グローバル標準に通用する書き方
データベースの命名は、後から変えるのが大変なため、最初に「チームで揃ったルール」を決めて従うのが重要です。世界的に広く使われている標準は次のようなものです。
テーブル名
-- 良い例
CREATE TABLE users (...);
CREATE TABLE products (...);
CREATE TABLE order_items (...);
CREATE TABLE user_addresses (...);
-- 悪い例
CREATE TABLE User (...); -- 大文字始まり
CREATE TABLE tbl_user (...); -- 不要な接頭辞 tbl_
CREATE TABLE ユーザー (...); -- 日本語 (一部 DBMS でしか動かない)
CREATE TABLE usr (...); -- 意味不明な略語
| ルール | 理由 |
|---|---|
複数形 (users 等) | テーブルはレコードの集合 (複数の users) を表す |
小文字 + snake_case | 多くの RDBMS で大文字小文字を区別する設定/しない設定があり、小文字統一が安全 |
| 略語を避ける | 半年後の自分や他人が見て意味が分かる |
| 日本語を避ける | DBMS / ツール / ライブラリの互換性問題を避ける |
カラム名
-- 良い例
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
price NUMERIC(10, 2),
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
is_active BOOLEAN NOT NULL DEFAULT TRUE -- Boolean は is_ で始める
);
-- 悪い例
CREATE TABLE products (
id INTEGER PRIMARY KEY,
Name VARCHAR(200), -- 大文字始まり
ProductPrice NUMERIC(10, 2), -- camelCase
作成日時 TIMESTAMPTZ, -- 日本語
flg BOOLEAN -- 意味不明な略語
);
グローバル標準に通用する命名
| 用途 | 推奨される名前 | 説明 |
|---|---|---|
| 主キー | id (or <tablename_singular>_id) | テーブル内一意の識別子。id 単独か、外部参照される場面が多いなら user_id のような明示形 |
| 作成日時 | created_at | レコード作成日時 |
| 更新日時 | updated_at | レコード最終更新日時 |
| 削除フラグ | is_deleted または deleted_at (日時で論理削除) | 論理削除に使う |
| 有効フラグ | is_active | 有効 / 無効の状態 |
| 表示順 | display_order または sort_order | 並び順制御 |
| 作成者 | created_by (ユーザーIDなど) | 監査ログ用 |
| 外部キー | <参照先テーブル単数_id> | orders.user_id のように参照先を明示 |
id か user_id か1 つのテーブル内で完結する文脈なら id でシンプルに、複数テーブルを JOIN する文脈なら users.id と orders.user_id のように外部キー側で参照先を明示する書き方が一般的です。Rails / Laravel などの ORM は主キーが id であることをデフォルトの前提にしているため、フレームワーク採用時はその慣習に従うのが安全です。
設計用語の俯瞰図
本ガイドの後の章で扱う用語の地図を示します。今すぐ全部覚える必要はなく、ここでは「こういう用語がある」と把握しておけば十分です。
実際のテーブル定義例
EC サイトの商品管理を例に、これまでの用語を確認してみましょう。
-- データベース作成 (通常はインフラ側で 1 回だけ)
CREATE DATABASE ecommerce_db;
-- スキーマ作成 (オプション)
CREATE SCHEMA shop;
-- カテゴリテーブル (マスターデータ)
CREATE TABLE shop.categories (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
parent_category_id INTEGER REFERENCES shop.categories(id), -- 自己参照外部キー
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- 商品テーブル
CREATE TABLE shop.products (
id SERIAL PRIMARY KEY,
code VARCHAR(50) UNIQUE NOT NULL,
name VARCHAR(200) NOT NULL,
description TEXT,
category_id INTEGER NOT NULL REFERENCES shop.categories(id),
price NUMERIC(10, 2) NOT NULL CHECK (price > 0),
stock_quantity INTEGER NOT NULL DEFAULT 0 CHECK (stock_quantity >= 0),
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- インデックス (検索の高速化)
CREATE INDEX idx_products_category ON shop.products(category_id);
CREATE INDEX idx_products_active ON shop.products(is_active);
-- ビュー (頻繁に書く SELECT を「見え方」として保存)
CREATE VIEW shop.active_products AS
SELECT p.id, p.name, p.price, c.name AS category_name
FROM shop.products p
JOIN shop.categories c ON p.category_id = c.id
WHERE p.is_active = TRUE
AND p.stock_quantity > 0;
ビューは「保存された SELECT 文」のようなものです。アプリ側からは普通のテーブルのように SELECT * FROM shop.active_products と書けますが、実体は元のテーブルから派生する仮想表です。同じ複雑なクエリを何度も書く代わりに、ビューを 1 つ作っておくとコードがスッキリします。
演習
次のテーブル定義の問題点を 4 つ以上指摘し、修正したテーブル定義を書いてみてください。
CREATE TABLE User (
UserID INTEGER PRIMARY KEY,
UserName VARCHAR(100),
Email VARCHAR(255),
ProductCount INTEGER,
flg BOOLEAN,
作成日 TIMESTAMPTZ
);
解答例
問題点と修正:
| 問題点 | 修正 |
|---|---|
テーブル名が大文字始まり・単数形 (User) | users (小文字・複数形) |
カラム名が camelCase (UserID, UserName) | id, name (snake_case) |
主キー名にテーブル名が冗長 (UserID) | id (テーブル内で完結) |
ProductCount が users テーブルにあるのは集計値で正規化に反する可能性 | カラム自体を削除し、SELECT COUNT(*) FROM products WHERE user_id = ? で都度集計 |
flg が意味不明な略語 | is_active のように意味のある Boolean 命名 |
作成日 が日本語 | created_at |
主キーが INTEGER (将来枯渇リスク) | SERIAL か BIGINT を使う |
NOT NULL 制約がない | 必須項目に NOT NULL を付ける |
修正版:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ありがちな間違い: product_count のような集計カラムを「便利だから」と気軽に追加すること。集計値は元データから計算で導けるため、保存すると元データと食い違うリスク (商品が追加・削除されたのに count が更新されない) があります。「集計値はクエリで都度算出する」が原則で、性能上どうしても必要なときだけ慎重に非正規化を入れます (詳しくは第 11 章 正規化)。
次の文の空欄を埋めてください。
- テーブル内で各レコードを一意に識別する値を [____] という。
- 別のテーブルの主キーを参照するカラムを [____] という。
- 複数のテーブルを論理的にまとめる名前空間を [____] という。
- 検索を高速化するためのデータ構造を [____] という。
- 既存テーブルから派生する「見え方」を保存したものを [____] という。
解答例
- 主キー (Primary Key, PK)
- 外部キー (Foreign Key, FK)
- スキーマ (schema)
- インデックス (index)
- ビュー (view)
補足: 4 のインデックスについて、「主キーには自動的にインデックスが作られる」点は覚えておくと役立ちます。SELECT * FROM users WHERE id = 1 のような主キー検索は常に高速です。逆に「主キー以外で頻繁に検索するカラム」(例: email, created_at) には明示的にインデックスを作る必要があります。
まとめ
この章で学んだことを整理します。
- データベースは「データベース → スキーマ → テーブル → カラム / レコード」の階層で構成される
- 用語には複数の呼び方がある (テーブル = 表 = 関係) ので、文献を読むときは慣れていく必要がある
- 命名規則は「小文字・snake_case・複数形 (テーブル)・略語回避・日本語回避」が世界的標準
- 集計値などの導出可能なカラムは安易に追加しない (正規化の原則、第 11 章で詳述)
次章では、データを 2 種類 (マスターデータとトランザクションデータ) に分類して扱う考え方を学びます。