MySQL のテーブル設計アンチパターン

テーブル設計にはやりがちな失敗パターンがいくつかあり、これらは「アンチパターン」として知られています。問題が顕在化するのは運用が始まってからのことが多く、そのときにはすでに修正コストが高くなっているのが厄介なところです。

EAV(Entity-Attribute-Value)

EAV は、属性名と値を行として格納するパターンです。一見すると柔軟に見えますが、深刻な問題を抱えています。

-- EAV アンチパターン
CREATE TABLE product_attributes (
  product_id INT NOT NULL,
  attribute_name VARCHAR(100) NOT NULL,
  attribute_value VARCHAR(255),
  PRIMARY KEY (product_id, attribute_name)
);

-- データ例
-- | product_id | attribute_name | attribute_value |
-- |------------|----------------|-----------------|
-- | 1          | brand          | Dell            |
-- | 1          | ram            | 16              |
-- | 1          | storage        | 512GB           |

この設計では、型安全性がなく(すべて VARCHAR)、制約が設定できず、クエリが複雑になります。1つの商品の全属性を取得するには自己 JOIN を繰り返すか、GROUP BY + CASE 文で横持ちに変換する必要があり、パフォーマンスも劣化します。属性が不定なデータには JSON 型を使うほうがはるかに実用的です。

マルチカラム属性

1つの概念に対して複数のカラムを横並びに用意するパターンです。

-- マルチカラム属性アンチパターン
CREATE TABLE contacts (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  phone1 VARCHAR(20),
  phone2 VARCHAR(20),
  phone3 VARCHAR(20)
);

電話番号が4つ以上必要になったとき ALTER TABLE が必要になり、「phone1 と phone2 のどちらにメインの番号が入っているか」が曖昧になります。正しくは別テーブルに分離すべきです。

-- 正しい設計
CREATE TABLE phones (
  id INT AUTO_INCREMENT PRIMARY KEY,
  contact_id INT NOT NULL,
  phone VARCHAR(20) NOT NULL,
  type ENUM('mobile', 'home', 'work') NOT NULL,
  is_primary BOOLEAN NOT NULL DEFAULT FALSE,
  FOREIGN KEY (contact_id) REFERENCES contacts(id)
);

ポリモーフィック関連

1つの外部キーカラムで複数のテーブルを参照しようとするパターンです。

問題のある設計

comments テーブルに commentable_type(‘post’ / ‘photo’)と commentable_id を持たせ、type に応じて参照先を切り替える。外部キー制約が設定できず、データベースレベルでの整合性保証が不可能。

改善策

テーブルごとに中間テーブルを作るか、共通の親テーブルを用意して単一テーブル継承で表現する。ORM が対応していても、データベース設計としては問題がある点を認識しておくべき。

カンマ区切りリスト

1つのカラムに複数の値をカンマ区切りで格納するパターンは、第1正規形に違反する典型的なアンチパターンです。

カンマ区切りの問題

特定の値を含むレコードの検索に LIKE や FIND_IN_SET() が必要で、インデックスが効かない。値の追加・削除が文字列操作になり、集計も困難。

正しい設計

多対多リレーションとして中間テーブルを作成する。検索・集計・制約のすべてが正常に機能し、インデックスも活用できる。

暗黙のデフォルト値

カラムに NOT NULL も DEFAULT も指定しないと、MySQL の SQL モードによっては暗黙のデフォルト値(空文字、0、‘0000-00-00’ など)が使われます。STRICT モードが無効な環境では、不正なデータが静かに挿入されるため、全カラムに NOT NULL / DEFAULT を明示するのが安全です。

ゴッドテーブル

すべての情報を1つの巨大テーブルに詰め込むパターンも危険です。カラム数が50を超えるようなテーブルは、関心事の分離ができていない兆候であり、テーブル分割を検討すべきです。

アンチパターンの多くは「短期的には楽」だが「長期的には苦しい」という性質を持っています。設計段階で少し手間をかけることが、運用フェーズでの大きなコスト削減につながります。