ドキュメント / 詳細設計 / データ系
•
PART 02 / 03
•
2026.06.18
•
20 min read
PART 02 — テーブル定義書
全カラム・制約・インデックスを網羅する物理設計書
テーブル定義書はER図を補完する「1テーブル1シート」の詳細仕様書だ。カラム説明・制約・インデックス・CRUD 参照情報まで1か所にまとめることで、実装とレビューの効率が大幅に向上する。
テーブル定義書の位置づけ
ER 図がテーブル間の「構造と関係」を示すのに対し、テーブル定義書は各テーブルの「詳細仕様」を定義する補完的なドキュメントだ。実装者がこの定義書だけ見てDDLを書けるレベルの精度が求められる。
⚠️ よくある不足定義
「VARCHAR(255)」のみで桁数の根拠・業務的最大値が不明、コード列の設定可能値が未定義、インデックスの作成根拠が不明、CRUD 情報がなくてどの処理が更新するか追跡できない — これらは後のバグの温床になる。
定義すべき項目一覧
| 分類 | 定義項目 | 必須度 |
| テーブル基本 | テーブル物理名・論理名・スキーマ名 | ◎ |
| テーブル基本 | テーブル説明・分類・保持期間 | ◎ |
| テーブル基本 | 想定レコード数(初期・1年後・5年後) | ○ |
| カラム | 列No・物理名・論理名・データ型・桁数 | ◎ |
| カラム | NULL可否・PK/FK/UK フラグ・デフォルト値 | ◎ |
| カラム | 設定可能値(コード値の場合) | ◎ |
| カラム | カラム説明・備考 | ◎ |
| 制約 | 制約名・種類・対象カラム・参照先(FK) | ◎ |
| インデックス | インデックス名・対象カラム・種別・作成理由 | ◎ |
| CRUD | このテーブルを CRUD する処理ID/名称 | ○ |
テーブル基本情報
| 項目 | 内容 |
| スキーマ名 | public |
| テーブル物理名 | products |
| テーブル論理名 | 商品マスタ |
| 分類 | マスタテーブル |
| テーブル説明 | 販売商品の基本情報・価格・在庫数を管理する。論理削除方式を採用する。 |
| 想定レコード数 | 初期: 5,000件 / 1年後: 20,000件 / 5年後: 100,000件 |
| 保持期間 | 無期限(論理削除で管理) |
| パーティション | なし |
カラム定義
カラム定義表(products テーブル)
No | 物理名 | 論理名 | 型 | 桁 | NULL | PK | FK | UK | デフォルト | 設定値/説明
---|-----------------|-------------|------------------|-----|------|----|----|----|-----------|-----------------------------------------
1 | product_id | 商品ID | BIGINT | — | × | ○ | | | AUTO | シーケンス自動採番
2 | category_id | カテゴリID | BIGINT | — | ○ | | ○ | | NULL | FK→categories.category_id
3 | product_code | 商品コード | VARCHAR | 20 | × | | | ○ | — | 英数字+ハイフン。自社採番ルール準拠
4 | product_name | 商品名 | VARCHAR | 200 | × | | | | — | 販売用の商品名称
5 | description | 商品説明 | TEXT | — | ○ | | | | NULL | 詳細説明文(HTML不可)
6 | unit_price | 単価 | NUMERIC |12,2 | × | | | | 0 | 税抜き単価(円)。0以上
7 | stock_quantity | 在庫数 | INTEGER | — | × | | | | 0 | 0以上。在庫引当時に排他ロック取得
8 | is_active | 販売フラグ | BOOLEAN | — | × | | | | true | false=販売停止
9 | created_at | 作成日時 | TIMESTAMPTZ | — | × | | | | NOW() | レコード作成日時
10 | updated_at | 更新日時 | TIMESTAMPTZ | — | × | | | | NOW() | 最終更新日時(トリガーで自動更新)
11 | deleted_at | 削除日時 | TIMESTAMPTZ | — | ○ | | | | NULL | NULL=有効。論理削除日時
制約定義
| 制約名 | 種別 | 対象カラム | 内容 |
| pk_products | PRIMARY KEY | product_id | 主キー制約 |
| uk_products_code | UNIQUE | product_code | 商品コードの重複禁止 |
| fk_products_cat | FOREIGN KEY | category_id → categories.category_id | ON DELETE SET NULL |
| chk_products_price | CHECK | unit_price | unit_price >= 0 |
| chk_products_stock | CHECK | stock_quantity | stock_quantity >= 0 |
インデックス定義
| インデックス名 | 対象カラム | 種別 | 作成理由 |
| uk_products_code | product_code | UNIQUE | 商品コード検索(WHERE product_code = ?) |
| idx_products_category | category_id | 通常 | カテゴリ別商品一覧取得 |
| idx_products_active | is_active(WHEREで部分) | 部分 WHERE is_active=true AND deleted_at IS NULL | 販売中商品の絞り込み高速化 |
CRUD 参照一覧
| 処理ID | 処理名 | C | R | U | D |
| API-003 | 商品登録API | ○ | | | |
| API-004 | 商品一覧取得API | | ○ | | |
| API-005 | 商品更新API | | | ○ | |
| API-006 | 商品削除API | | | ○(論理) | |
| BTH-002 | 在庫引当バッチ | | ○ | ○ | |
Python Tips — DDL の自動生成
Python — テーブル定義 dict から DDL を自動生成
"""
テーブル定義辞書から PostgreSQL DDL を自動生成する
"""
TABLE_DEF = {
"name": "products",
"columns": [
{"name":"product_id", "type":"BIGINT GENERATED ALWAYS AS IDENTITY", "nullable":False, "pk":True},
{"name":"category_id", "type":"BIGINT", "nullable":True},
{"name":"product_code", "type":"VARCHAR(20)", "nullable":False},
{"name":"product_name", "type":"VARCHAR(200)", "nullable":False},
{"name":"description", "type":"TEXT", "nullable":True},
{"name":"unit_price", "type":"NUMERIC(12,2)", "nullable":False, "default":"0"},
{"name":"stock_quantity", "type":"INTEGER", "nullable":False, "default":"0"},
{"name":"is_active", "type":"BOOLEAN", "nullable":False, "default":"true"},
{"name":"created_at", "type":"TIMESTAMPTZ", "nullable":False, "default":"NOW()"},
{"name":"updated_at", "type":"TIMESTAMPTZ", "nullable":False, "default":"NOW()"},
{"name":"deleted_at", "type":"TIMESTAMPTZ", "nullable":True},
],
"constraints": [
"CONSTRAINT uk_products_code UNIQUE (product_code)",
"CONSTRAINT fk_products_cat FOREIGN KEY (category_id) REFERENCES categories(category_id) ON DELETE SET NULL",
"CONSTRAINT chk_products_price CHECK (unit_price >= 0)",
"CONSTRAINT chk_products_stock CHECK (stock_quantity >= 0)",
]
}
def gen_ddl(tbl: dict) -> str:
cols = []
pk_cols = [c["name"] for c in tbl["columns"] if c.get("pk")]
for c in tbl["columns"]:
line = f' {c["name"]:<20} {c["type"]}'
if not c["nullable"]:
line += " NOT NULL"
if "default" in c:
line += f' DEFAULT {c["default"]}'
cols.append(line)
if pk_cols:
cols.append(f' CONSTRAINT pk_{tbl["name"]} PRIMARY KEY ({", ".join(pk_cols)})')
for con in tbl.get("constraints", []):
cols.append(f" {con}")
return f'CREATE TABLE {tbl["name"]} (
' + ",
".join(cols) + "
);"
print(gen_ddl(TABLE_DEF))
レビューチェックリスト
| # | チェック項目 |
| 1 | 全カラムに論理名・説明が記述されているか |
| 2 | コード値カラムに設定可能値一覧が記載されているか |
| 3 | 桁数に業務的な根拠があるか(最大値が検討されているか) |
| 4 | CRUD 一覧が定義されているか |
| 5 | 論理削除方針が定義されているか |
| 6 | 想定レコード数が記載されているか |