DB設計のアンチパターン『ポリモーフィック関連』をあえて採用した話
はじめに
この度、DB の論理設計でポリモーフィック関連を採用したところ、レビューで「それはアンチパターンだよ」とご指摘いただきました。
そもそも、ポリモーフィック関連がアンチパターンとされること自体を知らなかったことが原因です。
これを機にポリモーフィック関連について学習したうえで、最終的にはこの設計を本採用することにしました。この過程で学んだことを共有します。
実際の事例
今回の事例では、外部連携した商品について、紐づく商品がどんな種類であっても同じように扱いたいがために、下記のような外部連携商品(ExternalItems)テーブルを設計しました。

- 複数の種類の商品に対する外部キー(
linkable_id)を使用します。 - 加えて、文字列型の
linkable_type列を定義して、どの種類の商品を参照するかのメタデータを格納します。具体的にはコア商品CoreItem、限定商品LimitedItemなどが入る想定です。
この論理設計はポリモーフィック関連と呼ばれ、書籍『SQLアンチパターン』ではアンチパターンとされています*1。
なぜアンチパターンか
a. 外部キー制約を定義できない
外部キー制約は1つのテーブルしか指定できません。よって、linkable_idが親テーブルの値と一致するかをDBレベルで担保できません。
b. ER図で関連を表現できない
ER図で動的な参照は表現できません。ER 図を見たときに関連性がわかりづらく、後から設計を理解する人に不親切になります。
c. 親テーブルの情報を取得するクエリの実行が辛い
外部連携商品名の一覧が欲しい場合のクエリは下記になります。
SELECT e.*, c.name, l.name
FROM ExternalItems AS e
-- コア商品の結合
LEFT OUTER JOIN CoreItems AS c
ON (e.linkable_id = c.id AND e.linkable_type = 'CoreItem')
-- 限定商品の結合
LEFT OUTER JOIN LimitedItems AS l
ON (e.linkable_id = l.id AND e.linkable_type = 'LimitedItem');親テーブルが増えるごとにJOINを書かなければならなくなります。
また、実行結果は下記のように、どちらか一方の列が常に NULL になる形になります。
c.name (コア商品名) | l.name (限定商品名) |
|---|---|
バナナ | NULL |
NULL | 秋限定の柿 |
りんご | NULL |
アプリケーションコードで「どのカラムに値が入っているか」を判定しなければならなくなります。また、ソートやフィルタリングも工夫する必要が出てきます。
ORDER BY COALESCE(c.name, l.name) -- COALESCE等でNULLじゃない方を探す関数を使用する場合は、関数インデックスなどを張らない限りは基本的に通常の INDEX が効かないため、パフォーマンスにも影響がありそうです。
一般的な解決策
親ごとに交差テーブルを作成する
コア商品テーブルとの間に交差テーブル(CoreExternalItems)を作成します。

これを限定商品等、その他の商品の種類毎作成します。
a. 外部キー制約が定義できるようになる
外部キー制約をつけられるようになりDBレベルでidの値の一致を担保できるようになりました。
b. ER図で関連を表現できるようになる
上の図のように静的な関連を表現できます。
c. 親テーブルの情報を取得するクエリの実行は辛いまま
SELECT e.*, c.name, l.name
FROM ExternalItems AS e
-- コア商品の結合
LEFT OUTER JOIN CoreExternalItems AS ce
ON e.id = ce.external_item_id
LEFT OUTER JOIN CoreItems AS c
ON ce.core_item_id = c.id
-- 限定商品の結合
LEFT OUTER JOIN LimitedExternalItems AS le
ON e.id = le.external_item_id
LEFT OUTER JOIN LimitedItems AS l
ON le.limited_item_id = l.id;linkable_typeの指定をしなくて良くなりましたが、親テーブルが増えればJOINを増やす必要性は変わらなそうです。結果セットにもNULLが入ります(親種別ごとに UNION ALL でまとめる方法はある)。
共通の基底テーブルを作成する
コア商品と限定商品などのすべての商品が継承する基底テーブルを作成します。

a. 外部キー制約を定義できるようになる
こちらも、外部キー制約をつけられるようになります。
b. ER図で関連を表現できるようになる
上の図参照。
c. 親テーブルの情報を取得するクエリがシンプルになる
外部連携商品名の一覧を取得するクエリはどうでしょうか。
SELECT e.*, i.name
FROM ExternalItems e
JOIN Items i ON e.item_id = i.id; -- 結合先は常にItemsテーブル1つだけ結合先がItemsテーブルのみになり、クエリがシンプルになりました。取得したレコードごとにNULLが現れることもないので、アプリケーション側で判定ロジックが不要になります。また、INDEXを効かせたソートやフィルタリングも可能になります。
最終的にポリモーフィック関連を採用した理由
データ構造的に既存の商品テーブルに共通の基底テーブルを導入するコストが高かった
マイグレーションの手間だけでなく、既存処理への影響範囲も広く、今回の機能追加のスコープとしては現実的ではないと判断しました。
外部連携商品の振る舞いはどの商品でも同じだった
要件を整理すると、外部連携機能として必要なのは「外部連携商品として同じように扱えること」であり、その実態は意識する必要がありませんでした。よってポリモーフィック関連の動的な参照でも成立すると判断しました。
Eloquent のサポートによるメリットが大きいと判断した
このアプリケーションはバックエンドのフレームワークにLaravelを使用しており、LaravelのEloquentにはポリモーフィック関連を扱う機能がサポートされています*2。
class ExternalItem extends Model
{
public function linkable()
{
return $this->morphTo(); // CoreItem, LimitedItem などを透過的に扱える
}
}これにより、下記のメリットが得られます。
- 交差テーブル案に比べて、テーブル数・モデル数・リレーション定義を抑えられる
with('linkable')のように直感的な書き方でリレーションをロードできる- CRUD 処理や関連付けのコード量を減らせる
フレームワークへの依存度は上がりますが、このプロジェクトでは今後もLaravelを使い続ける予定であり、開発効率を優先するという判断をしています。
まとめ
本記事では、私の事例をみながら、ポリモーフィック関連の概要と採用した理由について書きました。もちろん、RDB のお作法だけを見ればポリモーフィック関連は推奨されない設計です。それでも今回は、要件・既存スキーマ・フレームワークの制約を踏まえたうえで、
「デメリットを理解したうえであえて選ぶ」判断をしました。
メリット・デメリットを理解したうえであれば、ポリモーフィック関連も1つの選択肢となり、状況によっては今回のように採用できるかもしれません。
参考にしていただけると幸いです!
*1: Bill Karwin 著、和田 卓人 監訳、児島 修 訳 『SQLアンチパターン 第2版』オライリー・ジャパン(2025)
*2: Laravel 公式ドキュメント「Eloquent: Relationships(Polymorphic Relationships)」, Laravel 12.x