Erdmates Logo
Erdmates
ブログ一覧へ
アンチパターンデータベース設計リファクタリングSQL

データベース設計のアンチパターン:よくある失敗と改善策

EAVモデル、ゴッドテーブル、過剰な正規化など、現場でよく見かけるデータベース設計のアンチパターンと、それぞれの適切な解決策を解説します。

2026年4月14日10 分で読めます

アンチパターンを学ぶ意義

「悪い設計」を知ることは「良い設計」を理解するよりも実践的な学びにつながります。アンチパターンは「一見うまくいきそうに見えるが、長期的に問題を引き起こす解決策」です。多くのアンチパターンは、初期要件では問題なく動作しますが、データ量の増加や要件の変化に対応できなくなります。

アンチパターン1:EAV(Entity-Attribute-Value)モデルの濫用

EAVは柔軟性のために属性をキーバリューで保存するパターンです。「将来どんな属性が追加されるかわからない」という状況で使われますが、クエリが複雑になりパフォーマンスが著しく低下します。

-- ❌ アンチパターン:EAVモデル
CREATE TABLE product_attributes (
  product_id  UUID NOT NULL,
  attr_name   VARCHAR(100) NOT NULL,
  attr_value  TEXT,
  PRIMARY KEY (product_id, attr_name)
);

-- "価格が1000円以下で、色が赤い商品"のクエリが地獄化
SELECT p.id, p.name
FROM products p
WHERE p.id IN (
  SELECT product_id FROM product_attributes
  WHERE attr_name = 'price' AND attr_value::NUMERIC <= 1000
)
AND p.id IN (
  SELECT product_id FROM product_attributes
  WHERE attr_name = 'color' AND attr_value = 'red'
);

-- ✅ 改善策1:具体的なカラムを使用
ALTER TABLE products
  ADD COLUMN price    DECIMAL(10,2),
  ADD COLUMN color    VARCHAR(50),
  ADD COLUMN weight   DECIMAL(8,2);

-- ✅ 改善策2:カテゴリ固有の属性はJSONBで管理(PostgreSQL)
ALTER TABLE products ADD COLUMN attributes JSONB;

CREATE INDEX idx_products_attributes ON products USING GIN(attributes);

-- クエリが直感的に
SELECT id, name FROM products
WHERE (attributes->>'price')::NUMERIC <= 1000
  AND attributes->>'color' = 'red';

アンチパターン2:ゴッドテーブル(God Table)

すべての情報を一つの巨大なテーブルに詰め込むパターンです。JOINを避けるために作られることが多いですが、保守性が著しく低下します。

-- ❌ アンチパターン:なんでもかんでも1テーブルに
CREATE TABLE everything (
  id              UUID PRIMARY KEY,
  type            VARCHAR(50),  -- 'user', 'order', 'product'
  user_name       VARCHAR(100),
  user_email      VARCHAR(255),
  order_total     DECIMAL(10,2),
  order_user_id   UUID,
  product_price   DECIMAL(10,2),
  product_category VARCHAR(50)
  -- NULL だらけになる
);

-- ✅ 改善策:正規化されたテーブル設計
CREATE TABLE users (
  id    UUID PRIMARY KEY,
  name  VARCHAR(100) NOT NULL,
  email VARCHAR(255) UNIQUE NOT NULL
);

CREATE TABLE products (
  id       UUID PRIMARY KEY,
  name     VARCHAR(200) NOT NULL,
  price    DECIMAL(10,2) NOT NULL,
  category VARCHAR(50)
);

CREATE TABLE orders (
  id         UUID PRIMARY KEY,
  user_id    UUID REFERENCES users(id),
  total      DECIMAL(10,2),
  created_at TIMESTAMP DEFAULT NOW()
);

アンチパターン3:ステータスコードのマジックナンバー

整数値で状態を管理するパターンは、コードの可読性を下げ、新しいメンバーの理解を阻みます。

-- ❌ アンチパターン:意味不明な数値
CREATE TABLE orders (
  id     UUID PRIMARY KEY,
  status INT  -- 1=pending, 2=processing, 3=shipped, 4=delivered, 5=cancelled
);

SELECT * FROM orders WHERE status = 3;  -- 3って何?

-- ✅ 改善策1:CHECK制約付きのENUM風VARCHAR
CREATE TABLE orders (
  id     UUID PRIMARY KEY,
  status VARCHAR(20) NOT NULL DEFAULT 'pending'
    CHECK (status IN ('pending', 'processing', 'shipped', 'delivered', 'cancelled'))
);

SELECT * FROM orders WHERE status = 'shipped';  -- 明確!

-- ✅ 改善策2:PostgreSQLのCREATE TYPE(ENUM)
CREATE TYPE order_status AS ENUM ('pending', 'processing', 'shipped', 'delivered', 'cancelled');

ALTER TABLE orders ADD COLUMN status order_status DEFAULT 'pending';

アンチパターン4:インデックスの過剰作成または不足

インデックスをほとんど作らない「インデックス貧乏」と、すべてのカラムにインデックスを作る「インデックス過多」の両方がアンチパターンです。

-- インデックスの使用状況を確認
SELECT
  schemaname,
  tablename,
  indexname,
  idx_scan AS times_used,
  pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
ORDER BY idx_scan ASC, pg_relation_size(indexrelid) DESC;

-- 使われていないインデックス(削除候補)
-- idx_scan = 0 で size が大きいもの

-- テーブルスキャンが多い箇所(インデックス追加候補)
SELECT relname, seq_scan, idx_scan,
       seq_scan - idx_scan AS seq_vs_idx
FROM pg_stat_user_tables
WHERE seq_scan > 100
ORDER BY seq_scan DESC;

Erdmatesで設計をレビュー

ErdmatesのERDビジュアライゼーションを使えば、ゴッドテーブルやEAVの濫用といった設計上の問題を一目で発見できます。テーブルのカラム数が異常に多い、あるいはほとんどのカラムがNULLABLEになっているといったアンチパターンのサインをERDで視覚化し、チームでレビューしましょう。

まとめ

アンチパターンは「悪意のある設計」ではなく、「当時は合理的に見えた解決策が長期的に問題を引き起こすもの」です。設計段階でのコードレビューとERDレビューを組み合わせ、アンチパターンを早期に発見・修正するプロセスを整備することが重要です。

Erdmatesで今すぐ始めましょう

AI搭載ERDエディタでデータベース設計を始めましょう。無料でご利用いただけます。

無料デモを試す無料で始める

関連記事

bigint・UUID v4・UUID v7:主キーの選び方(PostgreSQL 18 の uuidv7() 対応)

bigint・UUID v4・UUID v7 の主キーの長所と短所を比べ、PostgreSQL 18 に入った uuidv7() で変わった点と、状況別の選び方を整理します。

AIエージェントが読むデータベース:MCP時代のスキーマ設計原則

AIエージェントがMCPを通じてデータベースに直接接続するケースが増えています。エージェントがスキーマを誤って解釈しないように名前・コメント・制約を設計し、権限を安全に分ける方法を整理します。

PostgreSQL 19プレビュー:スキーマ設計と運用に影響する変更点(ベータ4時点)

正式リリースを控えたPostgreSQL 19の変更点のうち、テーブル設計・マイグレーション・運用に直接影響するものを公式リリースノート(ベータ4)に基づいて整理します。ベータで取り下げられた機能や、アップグレード前に確認すべき動作の変更もあわせて見ていきます。

Erdmates Logo
Erdmates

自然言語でデータベースを設計し、AI駆動ERDエディターでチームとリアルタイムでコラボレーション。

サービス

  • 機能紹介
  • 料金プラン
  • デモ
  • SQL→ERD変換

リソース

  • ブログ
  • ヘルプ
  • お問い合わせ

法的事項

  • 利用規約
  • プライバシーポリシー
  • 第三者提供
会社名: Produx|代表者: 최정회
住所: 강원특별자치도 원주시 솔우물2길 4-1 (무실동)
メール: master@erdmates.com
個人情報管理責任者: 최정회|ホスティングサービス提供者: Amazon Web Services

© 2026 Produx. All rights reserved.