テーブル定義書の読み方
テーブル定義書は、データベースのテーブル構造を定義したドキュメントです。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 | メールアドレス | 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 |
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 | - |
制約
インデックス
usersテーブル
| インデックス名 | カラム | 種別 | 備考 |
|---|---|---|---|
| PRIMARY | id | 主キー | - |
| uk_users_email | ユニーク | メールアドレスの一意制約 | |
| idx_users_status | status | 通常 | ステータス検索用 |
ordersテーブル
| インデックス名 | カラム | 種別 | 備考 |
|---|---|---|---|
| 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 |
補足:テーブル定義書の実例では、どのカラムが主キー・外部キーなのか、インデックスがどこに張られているのかが記載されています。これらの制約は、データの一意性を保証し、テーブル間の関連を管理するために重要な役割を果たします。
読み方のポイント
1. 主キー(PK)を確認する
- レコードを一意に識別するカラム
- JOINの条件に使う
- 通常は
idという名前
2. 外部キー(FK)を確認する
- 他テーブルとの関連
- JOINの条件に使う
- 参照整合性の制約
3. NULL可/不可を確認する
| NULL | 意味 | 実装への影響 |
|---|---|---|
| × | NULL不可 | 必須入力、INSERT時に必須 |
| ○ | NULL可 | 省略可能、NULLチェック必要 |
4. デフォルト値を確認する
- INSERT時に省略できるか
- どんな値が入るか
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のフィールド初期化で設定
関連ドキュメント
- 設計書・仕様書の読み方 - 設計書全般の読み方
- 詳細設計書の読み方 - 詳細設計書全般
- ER図の読み方 - テーブル間の関係
- API仕様書の読み方 - APIとの対応