T字形ER技法(佐藤正美)とExpand & Contractパターンを使ったPostgreSQLデータモデル設計スキル。 PK・FK・CASCADE制約の正確な設計も含む。「テーブル設計して」「ER図作って」「スキーマ変更したい」 「マイグレーション計画立てて」「データモデル考えて」「DB設計して」「正規化して」など、 データベース設計に関わるあらゆる場面で必ずこのスキルを使うこと。新規設計・既存スキーマの進化どちらにも対応する。
PostgreSQLを対象に、T字形ER技法による概念・論理設計と、Expand & Contractパターンによる 安全なスキーマ進化を組み合わせて実践する。PK・FK・CASCADE制約の正確な設計も行う。
業務データを「リソース系」と「イベント系」に分類してERモデルを設計する手法。 単なる技術的なテーブル分割ではなく、業務の実態を正確にモデルに反映させることが目的。
エンティティの分類
| 種類 | 意味 | 典型例 |
|---|---|---|
| リソース系(R) | 業務上管理される「もの」。比較的変化しない | 顧客、商品、従業員、拠点、部署 |
| イベント系(E) | 業務活動の発生を記録する「こと」。時系列に蓄積される | 注文、支払、入庫、出荷、契約 |
リソース系とイベント系の見極めが設計の根幹。「削除されうるか」「時系列に増えるか」を問うと判断しやすい。
設計の進め方
命名規則
customer, purchase_order){テーブル名}_id(例: customer_id)customer_id){テーブルA}_{テーブルB}(例: order_item)created_at, updated_at, deleted_at で統一既存スキーマを変更する際に、ダウンタイムなしで安全にマイグレーションするための3フェーズ手法。 各フェーズは独立してデプロイ可能であることが必須条件。
現状
│
▼
[Phase 1: Expand] 新しいカラム・テーブルを追加。古い要素はそのまま残す。
アプリは新旧両方に書き込む。
│
▼
[Phase 2: Migrate] 旧データを新スキーマへ移行するバッチを実行。
アプリは徐々に新スキーマを参照するよう切り替える。
│
▼
[Phase 3: Contract] 旧カラム・テーブルを削除。
アプリは新スキーマのみを使用している状態。
│
▼
新状態
各フェーズの設計原則
| フェーズ | DDLの特徴 | 注意点 |
|---|---|---|
| Expand | ALTER TABLE ADD COLUMN / CREATE TABLE | 新カラムは DEFAULT 値か NULL 許容にする。NOT NULL + DEFAULT なしは即時適用できない |
| Migrate | UPDATE / INSERT INTO ... SELECT | 大量データは LIMIT を使ってバッチ処理。ロングトランザクション回避 |
| Contract | ALTER TABLE DROP COLUMN / DROP TABLE | アプリのデプロイが完全に切り替わってから実行。ロールバック不能 |
制約はDBレベルでデータ整合性を担保する最後の砦。アプリに任せず、できる限りDB側で宣言する。
-- サロゲートキー(推奨デフォルト): ランダムUUID
customer_id UUID PRIMARY KEY DEFAULT gen_random_uuid()
-- 順序が必要な場合: BIGSERIAL(自動インクリメント)
log_id BIGSERIAL PRIMARY KEY
-- 複合主キー(中間テーブルなど)
PRIMARY KEY (order_id, product_id)
選択基準
| ケース | 推奨型 | 理由 |
|---|---|---|
| デフォルト | UUID | 分散環境・外部露出に安全、推測不能 |
| 順序が必要・パーティション対象 | BIGSERIAL | ソート・レンジ検索に有利 |
| 中間テーブル | 複合PK | 両方の外部キーを組み合わせた自然なPK |
-- 基本形: 明示的に制約名を付ける(エラー特定が容易になる)
CONSTRAINT fk_order_customer
FOREIGN KEY (customer_id)
REFERENCES customer (customer_id)
ON DELETE RESTRICT
ON UPDATE CASCADE
CASCADE オプションの選択指針
| オプション | 動作 | 使いどころ |
|---|---|---|
RESTRICT | 参照先が存在する限り削除・更新を拒否(デフォルト推奨) | リソース系→イベント系のほとんどのケース。誤削除を防ぐ |
CASCADE | 参照先の削除・更新に追随して削除・更新される | 明確な「所有」関係がある場合(例: 注文が消えたら注文明細も消す) |
SET NULL | 参照先が消えたら外部キーをNULLにする | 任意の関連(担当者が退職しても案件は残したい、など) |
SET DEFAULT | 参照先が消えたらデフォルト値をセット | デフォルトの代替リソースが存在する場合 |
NO ACTION | トランザクション終了時にチェック(RESTRICTとほぼ同じ) | 通常はRESTRICTを使う |
ON DELETE の設計判断フロー
参照元レコードは参照先なしに存在できるか?
└─ いいえ → CASCADE(所有関係)または RESTRICT(独立して管理)
└─ 参照先が消えたら参照元も不要か?
├─ はい → CASCADE
└─ いいえ → RESTRICT(削除前に参照元を先に処理する運用)
└─ はい(参照先は任意)→ SET NULL
ON UPDATE
ON UPDATE CASCADE を明示しておくと安全。例: 代表的なパターン
-- パターン1: リソース → イベント(RESTRICT)
CONSTRAINT fk_order_customer
FOREIGN KEY (customer_id) REFERENCES customer(customer_id)
ON DELETE RESTRICT ON UPDATE CASCADE,
-- パターン2: 親 → 子(CASCADE)
CONSTRAINT fk_order_item_order
FOREIGN KEY (order_id) REFERENCES "order"(order_id)
ON DELETE CASCADE ON UPDATE CASCADE,
-- パターン3: 任意の関連(SET NULL)
CONSTRAINT fk_task_assignee
FOREIGN KEY (assignee_id) REFERENCES employee(employee_id)
ON DELETE SET NULL ON UPDATE CASCADE,
-- パターン4: 中間テーブルの複合FK
CONSTRAINT fk_product_tag_product
FOREIGN KEY (product_id) REFERENCES product(product_id)
ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_product_tag_tag
FOREIGN KEY (tag_id) REFERENCES tag(tag_id)
ON DELETE CASCADE ON UPDATE CASCADE
-- NOT NULL: 業務上必須な属性には必ず付ける
name TEXT NOT NULL,
-- UNIQUE: 一意性が業務ルールである場合
email TEXT NOT NULL UNIQUE,
CONSTRAINT uq_user_tenant UNIQUE (email, tenant_id),
-- CHECK: 値の範囲・形式をDBで保証する
amount NUMERIC(12,2) NOT NULL CHECK (amount >= 0),
status TEXT NOT NULL CHECK (status IN ('draft', 'active', 'closed')),
-- DEFERRABLE: 同一トランザクション内で一時的に制約違反を許容(循環参照の解消など)
CONSTRAINT fk_xxx FOREIGN KEY (...) REFERENCES ...
DEFERRABLE INITIALLY DEFERRED
最初に用途を確認する:新規設計か既存スキーマの進化か。両方の場合は新規設計を先に行い、その後マイグレーション計画を立てる。
以下を確認してから設計に入る:
各外部キーについて CASCADE オプションを明示的に決定する。「デフォルトのまま」にしない。設計書に選択理由も記載する。
TIMESTAMPTZNUMERIC(precision, scale)(浮動小数点は使わない)deleted_at TIMESTAMPTZ。物理削除と混在させない| 変更の種類 | Expand & Contract 必要か |
|---|---|
| カラム追加(NULL許容 or DEFAULT付き) | 不要(1フェーズで適用可能) |
| カラム追加(NOT NULL、DEFAULTなし) | 必要 |
| カラム削除 | 必要 |
| カラム名変更 | 必要(追加→移行→削除) |
| テーブル分割・統合 | 必要 |
| 型変更 | 必要 |
| FK追加 | 既存データが制約を満たすか先に確認 |
変更を3フェーズに分解し、各フェーズのDDLとアプリ側対応を明示する。
各フォーマットの完全なテンプレートは references/formats.md を参照。
CREATE TABLE、外部キーのCASCADEオプション明示T字形ER技法
PK / FK / CASCADE
PostgreSQL物理設計
TIMESTAMPTZ を使用しているdeleted_at の扱いをアプリ側で合意しているExpand & Contract
NULL 許容または DEFAULT 付きになっている