📖 読了目安 約19分
この記事の目次
テーブル定義書の読み方
テーブル定義書は、データベースのテーブル構造を定義したドキュメントです。SQL作成やデータ操作で必ず参照します。
テーブル定義書とは
役割
- テーブル(データの入れ物)の構造を定義
- カラム(列)の仕様を定義
- 制約(主キー、外部キーなど)を定義
- インデックス(検索高速化)を定義
いつ使うか
| 場面 |
使い方 |
| SQL作成時 |
カラム名、型の確認 |
| 実装時 |
データ構造の理解 |
| テスト時 |
データ確認 |
| 障害調査時 |
データの追跡 |
テーブル定義書の構成
よくある構成要素
| 項目 |
内容 |
確認ポイント |
| テーブル名 |
物理名・論理名 |
命名規則 |
| カラム定義 |
各列の詳細 |
型、桁数、NULL |
| 主キー |
レコードを一意に識別 |
どのカラムか |
| 外部キー |
他テーブルとの関連 |
参照先 |
| インデックス |
検索高速化 |
どのカラムに |
| 備考 |
補足情報 |
運用ルール |
カラム定義の要素
| 項目 |
内容 |
確認すること |
| カラム名(物理名) |
実際のDB名 |
英数字、命名規則 |
| カラム名(論理名) |
日本語名 |
何のデータか |
| データ型 |
VARCHAR, INTなど |
格納できるデータ |
| 桁数 |
最大文字数/精度 |
入力値の制限 |
| NULL |
NULL許可/不可 |
必須かどうか |
| デフォルト |
初期値 |
未指定時の値 |
| 備考 |
補足説明 |
注意事項 |
よく使うデータ型
文字列型
| 型 |
説明 |
使い分け |
| CHAR(n) |
固定長文字列 |
桁数が決まっているもの(郵便番号など) |
| VARCHAR(n) |
可変長文字列 |
一般的な文字列 |
| TEXT |
長い文字列 |
備考、説明文など |
数値型
| 型 |
説明 |
使い分け |
| INT |
整数 |
ID、数量など |
| BIGINT |
大きい整数 |
大きなID、カウンターなど |
| DECIMAL(p,s) |
固定小数点 |
金額など精度が必要なもの |
| FLOAT |
浮動小数点 |
計算結果など |
日時型
| 型 |
説明 |
使い分け |
| DATE |
日付 |
生年月日など |
| TIME |
時刻 |
開始時刻など |
| DATETIME / TIMESTAMP |
日時 |
作成日時、更新日時など |
【サンプル】テーブル定義書の実例
usersテーブル定義書
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
テーブル定義書
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
■ 基本情報
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
テーブル名(物理) : users
テーブル名(論理) : ユーザー
スキーマ : public
説明 : サービス利用者の情報を管理
作成日 : 2024/01/15
更新日 : 2024/02/01
■ カラム定義
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
| No | 物理名 | 論理名 | データ型 | 桁数 | NULL | PK | FK | デフォルト | 備考 |
|----|--------|--------|----------|------|------|----|----|-----------|------|
| 1 | id | ユーザーID | BIGINT | - | × | ○ | - | AUTO | 自動採番 |
| 2 | email | メールアドレス | VARCHAR | 256 | × | - | - | - | ユニーク |
| 3 | password_hash | パスワード | VARCHAR | 256 | × | - | - | - | bcryptハッシュ |
| 4 | name | 氏名 | VARCHAR | 100 | × | - | - | - | - |
| 5 | phone | 電話番号 | VARCHAR | 15 | ○ | - | - | NULL | ハイフン含む |
| 6 | status | ステータス | INT | - | × | - | - | 1 | 1:有効, 2:停止, 9:退会 |
| 7 | last_login_at | 最終ログイン日時 | DATETIME | - | ○ | - | - | NULL | - |
| 8 | created_at | 作成日時 | DATETIME | - | × | - | - | CURRENT_TIMESTAMP | - |
| 9 | updated_at | 更新日時 | DATETIME | - | × | - | - | CURRENT_TIMESTAMP | ON UPDATE |
■ インデックス
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
| インデックス名 | カラム | 種別 | 備考 |
|---------------|--------|------|------|
| PRIMARY | id | 主キー | - |
| uk_users_email | email | ユニーク | メールアドレスの一意制約 |
| idx_users_status | status | 通常 | ステータス検索用 |
■ 外部キー
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
なし
■ 備考
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- パスワードは平文で保存しない(bcryptでハッシュ化)
- 退会時はstatus=9に更新(物理削除しない)
- statusの値は共通コードテーブルを参照
ordersテーブル定義書
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
テーブル定義書
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
■ 基本情報
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
テーブル名(物理) : orders
テーブル名(論理) : 注文
説明 : 注文情報を管理
■ カラム定義
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
| No | 物理名 | 論理名 | データ型 | 桁数 | NULL | PK | FK | デフォルト | 備考 |
|----|--------|--------|----------|------|------|----|----|-----------|------|
| 1 | id | 注文ID | BIGINT | - | × | ○ | - | AUTO | 自動採番 |
| 2 | user_id | ユーザーID | BIGINT | - | × | - | ○ | - | users.id |
| 3 | order_number | 注文番号 | VARCHAR | 20 | × | - | - | - | 'ORD-' + YYYYMMDD + 連番 |
| 4 | total_amount | 合計金額 | DECIMAL | 10,0 | × | - | - | 0 | 税込み |
| 5 | status | ステータス | INT | - | × | - | - | 1 | 後述 |
| 6 | ordered_at | 注文日時 | DATETIME | - | × | - | - | - | - |
| 7 | shipped_at | 発送日時 | DATETIME | - | ○ | - | - | NULL | - |
| 8 | created_at | 作成日時 | DATETIME | - | × | - | - | CURRENT_TIMESTAMP | - |
| 9 | updated_at | 更新日時 | DATETIME | - | × | - | - | CURRENT_TIMESTAMP | - |
■ ステータス値
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
| 値 | 意味 | 遷移元 | 遷移先 |
|----|------|--------|--------|
| 1 | 注文中 | - | 2, 9 |
| 2 | 決済完了 | 1 | 3, 9 |
| 3 | 出荷準備中 | 2 | 4, 9 |
| 4 | 発送済み | 3 | 5 |
| 5 | 配達完了 | 4 | - |
| 9 | キャンセル | 1, 2, 3 | - |
■ インデックス
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
| インデックス名 | カラム | 種別 | 備考 |
|---------------|--------|------|------|
| PRIMARY | id | 主キー | - |
| uk_orders_number | order_number | ユニーク | 注文番号の一意制約 |
| idx_orders_user | user_id | 通常 | ユーザー検索用 |
| idx_orders_status | status | 通常 | ステータス検索用 |
| idx_orders_ordered | ordered_at | 通常 | 日付検索用 |
■ 外部キー
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
| 制約名 | カラム | 参照先 | ON DELETE | ON UPDATE |
|--------|--------|--------|-----------|-----------|
| fk_orders_user | user_id | users(id) | RESTRICT | CASCADE |
■ 備考
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- 注文番号は業務で使う識別子(ORD-20240115-0001形式)
- ユーザーが退会してもordersは残る(RESTRICT)
- キャンセル後の復活はできない
読み方のポイント
1. 主キー(PK)を確認する
- レコードを一意に識別するカラム
- JOINの条件に使う
- 通常は
idという名前
2. 外部キー(FK)を確認する
- 他テーブルとの関連
- JOINの条件に使う
- 参照整合性の制約
3. NULL可/不可を確認する
| NULL |
意味 |
実装への影響 |
| × |
NULL不可 |
必須入力、INSERT時に必須 |
| ○ |
NULL可 |
省略可能、NULLチェック必要 |
4. デフォルト値を確認する
5. インデックスを確認する
- どのカラムに検索条件がつくか
- ユニーク制約があるか
SQLを書くときの活用
SELECT文の例
-- usersテーブルから有効なユーザーを取得
SELECT id, email, name, phone
FROM users
WHERE status = 1
ORDER BY created_at DESC;
JOIN文の例
-- ordersとusersを結合
SELECT o.id, o.order_number, o.total_amount,
u.name, u.email
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = 2;
INSERT文の例
-- usersに新規レコード追加
INSERT INTO users (email, password_hash, name, phone)
VALUES ('user@example.com', 'hashed_password', '山田太郎', '03-1234-5678');
-- id, status, created_at, updated_at はデフォルト値が入る
よくある疑問
Q: 物理名と論理名の違いは?
- 物理名:実際のDBで使う名前(英数字)
- 論理名:人間が理解するための名前(日本語)
Q: ユニーク制約とは?
同じ値のレコードを入れられない制約。メールアドレスなど重複不可の項目に設定。
Q: ON DELETE RESTRICT とは?
参照先のレコードを削除しようとしたとき、参照しているレコードがあるとエラーになる設定。
| 設定 |
動作 |
| RESTRICT |
削除不可(エラー) |
| CASCADE |
一緒に削除 |
| SET NULL |
NULLに更新 |
テーブル定義書でよくある落とし穴
| 落とし穴 |
対策 |
| カラム名を間違える |
コピペで確実に |
| 型の違いを見落とす |
型を確認してから実装 |
| NULL可を見落とす |
NULLハンドリングを忘れない |
| 桁数制限を見落とす |
桁数を確認して入力値をチェック |
【実践】テーブル定義書からエンティティを作る
サンプルのusersテーブル定義書から、Javaのエンティティクラスを作成してみましょう。
ステップ1:基本構造を決める
テーブル基本情報を見て、クラスの基本構造を決めます。
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "users") // テーブル名(物理): users
public class User {
// カラム定義をもとにフィールドを定義
}
ステップ2:主キーを設定する
カラム定義のPK列を見て、主キーを設定します。
// No.1: id - BIGINT, PK, AUTO
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
ステップ3:各カラムをフィールドにマッピングする
@Entity
@Table(name = "users")
public class User {
// No.1: id - BIGINT, PK, AUTO
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// No.2: email - VARCHAR(256), NOT NULL, ユニーク
@Column(nullable = false, length = 256, unique = true)
private String email;
// No.3: password_hash - VARCHAR(256), NOT NULL
@Column(name = "password_hash", nullable = false, length = 256)
private String passwordHash; // スネークケース→キャメルケース変換
// No.4: name - VARCHAR(100), NOT NULL
@Column(nullable = false, length = 100)
private String name;
// No.5: phone - VARCHAR(15), NULL許可
@Column(length = 15)
private String phone; // nullableはデフォルトtrue
// No.6: status - INT, NOT NULL, デフォルト1
@Column(nullable = false)
private Integer status = 1; // デフォルト値を設定
// No.7: last_login_at - DATETIME, NULL許可
@Column(name = "last_login_at")
private LocalDateTime lastLoginAt;
// No.8: created_at - DATETIME, NOT NULL, デフォルトCURRENT_TIMESTAMP
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;
// No.9: updated_at - DATETIME, NOT NULL, ON UPDATE
@Column(name = "updated_at", nullable = false)
private LocalDateTime updatedAt;
// ライフサイクルコールバック
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();
updatedAt = LocalDateTime.now();
}
@PreUpdate
protected void onUpdate() {
updatedAt = LocalDateTime.now();
}
// getter/setter は省略
}
カラム定義とアノテーションの対応表
| カラム定義の項目 |
JPAアノテーション |
例 |
| PK |
@Id + @GeneratedValue |
主キー |
| NOT NULL(×) |
@Column(nullable = false) |
必須カラム |
| NULL許可(○) |
@Column(nullableは省略可) |
任意カラム |
| 桁数 |
@Column(length = n) |
VARCHAR(256) |
| ユニーク制約 |
@Column(unique = true) |
メールアドレスなど |
| 物理名がJava命名規則と異なる |
@Column(name = "...") |
snake_case → camelCase |
| デフォルト値 |
フィールド初期化 |
= 1 |
外部キーがある場合
ordersテーブルのuser_id(FK)を例に、リレーションを設定します。
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// No.2: user_id - BIGINT, FK → users(id)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
@Column(name = "order_number", nullable = false, length = 20, unique = true)
private String orderNumber;
@Column(name = "total_amount", nullable = false)
private BigDecimal totalAmount;
@Column(nullable = false)
private Integer status = 1;
@Column(name = "ordered_at", nullable = false)
private LocalDateTime orderedAt;
@Column(name = "shipped_at")
private LocalDateTime shippedAt; // NULL許可
// ... created_at, updated_at
}
ステータス値の対応
テーブル定義書にステータス値の一覧がある場合、Enum化すると便利です。
// ordersテーブルのステータス値をEnumに
public enum OrderStatus {
ORDERING(1, "注文中"),
PAID(2, "決済完了"),
PREPARING(3, "出荷準備中"),
SHIPPED(4, "発送済み"),
DELIVERED(5, "配達完了"),
CANCELLED(9, "キャンセル");
private final int code;
private final String label;
OrderStatus(int code, String label) {
this.code = code;
this.label = label;
}
public int getCode() { return code; }
public String getLabel() { return label; }
public static OrderStatus fromCode(int code) {
for (OrderStatus status : values()) {
if (status.code == code) return status;
}
throw new IllegalArgumentException("Unknown code: " + code);
}
}
テーブル定義書からエンティティを作るときのポイント
- 物理名と論理名:物理名(スネークケース)をDBに、論理名をコメントに
- NULL可否:NOT NULLのカラムには
nullable = falseを付ける
- 桁数:VARCHARの桁数は
lengthで指定
- 外部キー:
@ManyToOne/@OneToManyで関連を表現
- デフォルト値:Javaのフィールド初期化で設定
関連ドキュメント