テーブル設計で気をつけるべきことまとめ

### はじめに

「テーブル設計は最初にしっかり考えないといけない」と言われながらも、プロジェクトの初期には機能要件の変化や納期のプレッシャーなどもあって、つい後回しになりがちです。しかし、テーブル設計を甘く見ていると、あとから大規模リファクタリングが必要になったり、パフォーマンス問題が噴出したりといったリスクが高まります。

本記事では、テーブル設計において意識するべき代表的なポイントをピックアップしてご紹介します。

### 1. 正規化・非正規化のバランスを考える

#### 1.1 正規化の基本
- 正規化とは、データの冗長性や不整合を防ぐために、テーブルをルール（正規形）に基づいて整理すること。
- 一般的には「第1正規形 (1NF) → 第2正規形 (2NF) → 第3正規形 (3NF) → …」と段階的に進めます。
- 重複データや更新時の不整合を避けるうえで重要。

#### 1.2 非正規化の判断
- 過度な正規化はJOINが増え、パフォーマンスが悪化するケースも。
- 大量データの読み込みが中心のテーブルでは、非正規化して読み込みを高速化する設計も視野に入れる。
- ただし、非正規化したら更新系をシンプルに保てるよう工夫が必要（トリガーやバッチなどで整合性を保つ）。

### 2. 適切な主キー (Primary Key) の設定

#### 2.1 主キーとは
- テーブルの行を一意に識別するためのキー。
- アプリケーションや他のテーブルが参照する可能性が高いため、慎重に選定したい。

#### 2.2 数値型のサロゲートキー vs. 業務的なナチュラルキー
- **サロゲートキー（例: AUTO_INCREMENT, シーケンス、UUIDなど）**
  - データベース側で一意な値を自動生成。
  - レコード追加・更新がしやすい一方で、業務的には意味のないID。
- **ナチュラルキー（例: メールアドレス、ISBN、運転免許証番号など）**
  - 業務ドメインで一意性が担保される。
  - 変更要求が起きる可能性や、文字列が長すぎる場合などは要注意。

#### 2.3 クラスタリングやパフォーマンスへの影響
- 一部のDB (MySQL(InnoDB)など) では、主キーがクラスタインデックスとして物理的な並び順を規定する。
- 主キーにランダムなUUIDを使う場合、INSERTの際にページ分割が頻発し、パフォーマンスが落ちることもある。
- 数値連番のほうがアクセスパターンが安定しやすいケースが多い。

### 3. 適切なデータ型を選択する

#### 3.1 基本データ型の選択
- **文字列を何でもVARCHAR(255)にしない**
  - 文字列型は必要な長さを把握したうえで最適なサイズを選択（短ければメモリやディスクが節約できる）。
- **数値型は範囲を確認**
  - INT, BIGINT, DECIMALなど、将来的なデータの桁数増大を見込んで適切に選択。
- **日付型も正しく使う**
  - DATEかTIMESTAMPかDATETIMEか、タイムゾーンをどう扱うかなどを検討。

#### 3.2 ENUMやBOOLEANの扱い
- ENUM(列挙型) は可読性が上がる反面、変更が発生するとALTER TABLEが必要などのデメリットも。
- BOOLEANまたは**TINYINT(1)**など、真偽値を直接表現できる型があれば利用する(ただしDBによっては未対応)。

#### 3.3 バイナリデータ・大きなテキスト
- BLOB, CLOB, TEXT系カラムは大量データになる可能性が高く、DB内に保持するとパフォーマンス問題を引き起こす場合も。
- 場合によっては外部ストレージ (オブジェクトストレージ等) を利用し、DBにはパスやキーのみを格納する設計も検討。

### 4. インデックス設計

#### 4.1 必要なインデックスの洗い出し
- WHERE句・JOIN句・ORDER BY句で多用されるカラムに対してインデックスを検討。
- 何でもかんでも張りすぎると、INSERT/UPDATE/DELETE時の負荷が増大するので注意。

#### 4.2 複合インデックスとカーディナリティ
- 複数カラムを組み合わせる場合、よく使われる順番で作る。
- カラムのカーディナリティ（重複度合い）を意識し、絞り込み効果が高い（ユニークに近い）カラムを先頭に持ってくると効果的なケースが多い。

#### 4.3 リレーショナルDB以外の選択肢
- 非構造化データや全文検索が必要な場合は、ElasticsearchやMongoDBなどのNoSQL系DB、またはMySQL/PGの全文検索インデックスなどを検討する。
- 要件に合わせてインデックス設計を柔軟に。

### 5. 命名規則と可読性

#### 5.1 テーブル名・カラム名の命名規則
- 命名規則をあらかじめ定義しておき、プロジェクト全体で統一する。
- テーブル名は単数形か複数形か、カラム名の接頭辞/接尾辞のルール、アンダースコア区切り or キャメルケース などを明確にする。
- 業務で使う英単語の綴りを統一しておく（例: colour / color、organisation / organization など）。

#### 5.2 意味が分かるカラム名
- 業務的な意味が伝わるようなネーミングを心がける。
- 例: created_at, updated_at, is_active など、役割が明確な名前を付ける。

### 6. NULLの扱い

#### 6.1 NULLを許可するか
- NOT NULL制約を積極的に使って、NULL値を必要以上に許容しないほうがデータ品質を保ちやすい。
- どうしてもNULLが必要な場合はビジネスロジック上での意味をハッキリ定義しておく。

#### 6.2 デフォルト値とチェック制約
- NULLではなくデフォルト値を入れるほうが望ましいケースもある（例: 数量、フラグなど）。
- CHECK制約を使うとデータが正しく入力されることをDBレベルで保証できる(ただしDB製品によって機能差がある)。

### 7. リレーション(外部キー)の扱い

#### 7.1 外部キー制約を貼るかどうか
- 外部キー(FK)制約を貼ると、DBが参照整合性を自動的に管理してくれる利点がある。
- しかし、外部キーを多用するとテーブル間の依存関係が複雑になり、INSERT/DELETE時のパフォーマンスに影響が出ることも。

#### 7.2 ON DELETE CASCADE / ON UPDATE CASCADE
- 関連する行を自動的に削除・更新してくれる機能。
- メリット: アプリケーションロジックがシンプルになる。
- デメリット: 思わぬ連鎖削除を起こさないように注意。


### 8. パーティショニング(大規模データ向け) (続き)
#### 8.1 パーティショニングの利点
- データ量が非常に大きくなるテーブルでは、パーティショニングによってデータを分割し、高速化や運用を容易にできる（古いパーティションをまとめてアーカイブするなど）。
- 例: 日付で区切ってパーティションを作る。

#### 8.2 留意点
- パーティショニングキーをどう設定するかが重要。
- 適切でないパーティショニングをすると逆にパフォーマンスが下がる可能性も。
- DB製品やエンジン（MySQL Partitioning, PostgreSQL Partitioning, Oracle Partitioningなど）の制約を把握しておく。

### 9. セキュリティとプライバシー

#### 9.1 個人情報の取り扱い
- 住所・メールアドレス・電話番号などの個人情報は厳重に管理する必要がある。
- データの取り扱いポリシーや暗号化が求められるケースも多い。

#### 9.2 アクセス権限とビュー
- テーブル単位のアクセス制御を行うか、ビューやストアドファンクションを介してデータにアクセスさせるか検討。
- 開発時からセキュリティを念頭に置き、ユーザー/ロールごとの権限を整理すること。

### 10. まとめ
- 正規化 / 非正規化のバランスを意識し、冗長性とパフォーマンスを両立する。
- 主キー・外部キーを慎重に設計し、テーブル間の依存関係を明確に。
- データ型を正しく選び、必要に応じてインデックスを最適に配置する。
- NULLの扱いやデフォルト値、制約設定でデータの品質や意味を保つ。
- 命名規則や可読性を大切にし、将来の拡張や保守がしやすい設計を心がける。
- 大規模データやセキュリティを意識した設計オプション（パーティショニングや暗号化など）も検討する。

テーブル設計の段階で抑えておくべきことは多岐にわたりますが、ここをしっかり設計しておくと、後々の運用や拡張が圧倒的に楽になります。
プロジェクトの初期に時間が取りづらくても、ある程度の設計指針とルールを作り、最低限の整合性と将来拡張の下地を固めるだけでも大きく変わってきます。

### おわりに

本記事では、テーブル設計における代表的な注意点をまとめました。プロジェクトごとに最適解は異なりますが、今回挙げたポイントを「デザインチェックリスト」として活用していただければ幸いです。もし気になる点や追加で知りたいことがあれば、ぜひコメントや別途ご連絡ください！

以上、テーブル設計において気をつけるべきことのまとめでした。皆さんの設計がよりスムーズに進みますように。