LoginSignup
8
2

More than 3 years have passed since last update.

SQLアンチパターン ポリモーフィック関連

Last updated at Posted at 2020-07-30

ポリモーフィック関連

子テーブルが複数の親をテーブルを参照するアンチパターン

#アンチパターン例

以下の例では親テーブルとしてcomicテーブルが定義されており、漫画本1冊についてcommentテーブルで本のコメントがつけられている状態である。

comic

comic_id comment_id name
1 1 エンジニアになろう1巻
2 2 エンジニアになろう2巻
3 3 エンジニアになろう3巻

comment

comment_id comment
1 なかなか好調な滑り出し
2 2巻からこの盛り上がりはすごい!
3 どうしてこうなった....

アンチパターンを用いた場合、親テーブルがcomicテーブル以外にも存在し、それぞれがcommentテーブルでコメントを紐づけている状態を指す。

comic

comic_id comment_id name
1 1 エンジニアになろう1巻
2 2 エンジニアになろう2巻
3 3 エンジニアになろう3巻

magazine

magazine_id comment_id name
1 4 週刊エンジニア7月号
2 5 週刊エンジニア8月号
3 6 週刊エンジニア9月号

comment

comment_id comment
1 なかなか好調な滑り出し
2 2巻からこの盛り上がりはすごい!
3 どうしてこうなった....
4 あの超大御所エンジニアがこんなことを.....
5 あの有名エンジニアがそんなことを.....
6 あのエンジニアがあんなことを.....

このようなテーブル構成の場合、commentテーブルのcomment_idに外部キーを設定しようとしても親テーブルが複数あるため外部キーを設定できない状態となる。

そもそも外部キーって?

他のテーブルのデータにを参照(依存)するようにカラムにつける制約。

  • 親テーブル ⇨ 子テーブルに参照されるテーブル
  • 子テーブル ⇨ 親テーブルを参照するテーブル

具体的に設定するとどうなるのか以下の例を見て欲しい。

外部キーは、親テーブルに存在しない値の登録ができない

親テーブル

book_id (PK) title
1 エンジニアの本1
2 エンジニアの本2
3 エンジニアの本3

子テーブル

comment_id (PK) book_id (FK) comment
1 1 とても良い
2 2 まあまあ良い
3 3 どちらかと言えば良い
4 4 どちらかと言えばよくない

↑ 追加したい

この場合、子テーブルに4つ目のデータを登録しようとしましたが親テーブルに「book_id が 4」のデータがないためデータを登録できない。

外部キーは、子テーブルに存在する値の削除できない

親テーブル

book_id (PK) title
1 エンジニアの本1
2 エンジニアの本2
3 エンジニアの本3

↑ 削除したい

子テーブル

comment_id (PK) book_id (FK) comment
1 1 とても良い
2 2 まあまあ良い
3 3 どちらかと言えば良い

この場合、親テーブルから「book_id が 3」のデータを削除しようとしましたが子テーブルで「book_id が 3」を参照しているため削除できない。

削除する場合、子テーブルから「book_id が 3」を参照しているデータを削除してから親テーブルのデータを削除する必要がある。

外部キーを設定することで

上記の例のように親がいないデータの登録、子のいる親の削除などができないため依存関係のあるテーブル間のデータに対して整合性を担保できる。

デメリット

外部キーを設定できない。
何度か説明しましたが、commentテーブルの親テーブルが複数存在するため外部キーを設定することができない。
それにより、どちらの親テーブルに紐づいているのか、そもそも親のデータが存在しているのか、データの更新時に整合性が取れているのかなど懸念点が多い。

解決策

参照を逆にする

comic

comic_id name
1 エンジニアになろう1巻
2 エンジニアになろう2巻
3 エンジニアになろう3巻

magazine

magazine_id name
1 週刊エンジニア7月号
2 週刊エンジニア8月号
3 週刊エンジニア9月号

comment

comment_id book_id book_type comment
1 1 comic なかなか好調な滑り出し
2 2 comic 2巻からこの盛り上がりはすごい!
3 3 comic どうしてこうなった....
4 1 magazin あの超大御所エンジニアがこんなことを.....
5 2 magazin あの有名エンジニアがそんなことを.....
6 3 magazin あのエンジニアがあんなことを.....

こちらも外部キーを設定できないデメリットはある。

しかしORMを使用していて以下のようなパターンでは意識的に選択することもある。

  • ポリモーフィック関連の子側が複数の案件で共通して使われるフレームワークで、親側が個々の案件のアプリケーションである場合
  • ポリモーフィック関連の子側がパッケージ製品で、親側がパッケージ購入者のアプリケーションである場合
  • 関連の子側のテーブルと親側のテーブルの作成時期が離れているときなどである。

上記のパターンに当てはまらない場合は使用は推奨されない。

交差テーブルの作成

comic

comic_id name
1 エンジニアになろう1巻
2 エンジニアになろう2巻
3 エンジニアになろう3巻

magazine

magazine_id name
1 週刊エンジニア7月号
2 週刊エンジニア8月号
3 週刊エンジニア9月号

comicComment

comic_id comment_id
1 1
2 2
3 3

magazineComment

magazine_id comment_id
1 4
2 5
3 6

comment

comment_id comment
1 なかなか好調な滑り出し
2 2巻からこの盛り上がりはすごい!
3 どうしてこうなった....
4 あの超大御所エンジニアがこんなことを.....
5 あの有名エンジニアがそんなことを.....
6 あのエンジニアがあんなことを.....

交差テーブルではcommentテーブルの外部キーに加え、各親テーブルにも外部キーが定義することができ、データの整合性を取ることができる。

しかし両方の交差テーブルから参照された場合対処できないというデメリットもある

共通の親テーブル作成

book

book_id comment_id
1 1
2 2
3 3
4 4
5 5
6 6

comic

book_id
1
2
3

magazine

book_id
4
5
6

comment

comment_id comment
1 なかなか好調な滑り出し
2 2巻からこの盛り上がりはすごい!
3 どうしてこうなった....
4 あの超大御所エンジニアがこんなことを.....
5 あの有名エンジニアがそんなことを.....
6 あのエンジニアがあんなことを.....

comicテーブルとmagazineテーブルの主キーはそれぞれ外部キーとなっておりbookテーブルの主キーの値と同じになっているためcomicテーブルとmagazineテーブルを直接commentテーブルに接続できる。

しかしこちらも両方の交差テーブルから参照された場合対処できないというデメリットがある

その他

book

book_id book_type name
1 comic エンジニアになろう1巻
2 comic エンジニアになろう2巻
3 comic エンジニアになろう3巻
4 magazine 週刊エンジニア7月号
5 magazine 週刊エンジニア8月号
6 magazine 週刊エンジニア9月号

comment

comment_id comment
1 なかなか好調な滑り出し
2 2巻からこの盛り上がりはすごい!
3 どうしてこうなった....
4 あの超大御所エンジニアがこんなことを.....
5 あの有名エンジニアがそんなことを.....
6 あのエンジニアがあんなことを.....

親テーブル(comicやmagazine)のように動的に追加される可能性がある、そもそも複数の種類を格納する可能性がある場合は上記のような方法が一番シンプルではないかと思う。

もう一つの方法として以下のようなパターンも検討できる。
親テーブルごとに子のcommentテーブルを作成し外部キーを付与するという方法である。

comic

comic_id name
1 エンジニアになろう1巻
2 エンジニアになろう2巻
3 エンジニアになろう3巻

magazine

magazine_id name
1 週刊エンジニア7月号
2 週刊エンジニア8月号
3 週刊エンジニア9月号

comicComment

comic_id comment
1 あの超大御所エンジニアがこんなことを.....
2 あの有名エンジニアがそんなことを.....
3 あのエンジニアがあんなことを.....

magazineComment

magazine_id comment
1 あの超大御所エンジニアがこんなことを.....
2 あの有名エンジニアがそんなことを.....
3 あのエンジニアがあんなことを.....

まとめ

設計の段階で参照元と参照先が1つであることが大切であると感じた。
やむを得ない場合であっても親を複数持つパターンは整合性の担保という意味でも推奨されない。

次回

マルチカラムアトリビュート

参考文献

SQLアンチパターン

参考文献.jpg

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2