RDBにて1対Nの関係を表す際のアンチパターンとそのデメリットについて見ていきます。
アンチパターンとそのデメリット
1対多をリレーションではなく、カラムで表現してはいけません。
例えば、記事に複数のタグが紐づくようなデータ構造を考えていきます。
この際に、以下のようなデータ構造で表現をしてはいけません。
これにより、以下のようなデメリットが生じるからです。
検索クエリが冗長になる
タグの条件で記事を絞り込む際に、全てのタグカラムを検証する必要があります。
例えば、tag=1が紐づいている記事を見る場合に、記事テーブルのtag1、tag2、tag3...と全てのカラムを見ないといけません
そのため、クエリが冗長になります。
// tag1が紐づいている記事を検索する
SELECT *
FROM ARTICLE
WHERE TAG1 = '1' OR TAG2 = '1' OR TAG3 = '1' ...
また、タグカラムが追加された場合は、この形式のクエリ全てに対してWHERE条件文を変更しないといけません。
タグ追加時の手間が増える
タグを追加するために以下の3つの作業を行う必要があります。
・どのカラムが空いているかを検索する。
・同じタグが紐づいていないかを検証する。
・空いているカラムにタグを挿入する。
ただタグを追加するためだけに、一つの作業につきクエリが3回発行されます。
そのため、そのトランザクション中、競合が発せしないように対象記事にロックをかける必要があります。
これは、パフォーマンス劣化を引き起こす可能性があります。
重複タグの検証が手間
DBの機能で記事に同一タグが複数関連付けられることを回避、検証することができません。
複数カラム間で同じ値が存在するかを検証する機能がRDBには無いからです。
これを検証するためには、専用のチェックスクリプトを流すなどしないといけません。
最大タグ数を増やすためにDDLを流さないといけない
この構造だと記事に紐づけられる最大タグ数が決まっています。
増やすためには、ALTER文を流し、記事テーブルにタグカラムを追加しないといけません。
テーブル構造の変更は、システム停止などを伴う、リスクの高い作業になります。
また、テーブル構造が変化するため、記事テーブルを使用しているすべてのクエリに対して変更を加える必要も出てきます。
解決策
タグと記事の紐づけ用の関連テーブルを用意します。
こうすることでRDBの機能で1対Nの関連を表現できます。
そうすることで一意性のチェックを使えたり、クエリをシンプルに書くことができます。
// タグ1が関連づく記事を抽出するクエリ
SELECT *
FROM ARTICLE A
INER JOIN TAG_ARTICLE TA
ON A.ID = TA.ARTICLE_ID
INNER JOIN TAG T
ON T.ID = TA.TAG_ID
WHERE T.ID = '1';
タグの追加もタグ記事テーブルへのINSERT文一つで済みます。
重複は、タグ記事テーブルへのユニーク制約によって防ぐことができます。
アンチパターンを用いてもよい例
それぞれのカラムが異なる意味合いを持つ場合は、このパターンを用いても問題ありません。
例えば、記事データに対して、執筆者、承認者、投稿者のようなカラムが存在し、それぞれaccount_idが入る場合などです。
同じ形式のデータが入るが、それぞれの意味合いが異なるので、記事テーブルに定義しても問題ありません。
同じ用途で使用されるカラムを複数定義するのが問題になるのです。