😀

データベースデザインの罠:アンチパターンとは?

2024/06/18に公開

1. はじめに

データベースの設計は、システムの効率性や柔軟性に大きな影響を与えます!この記事では、主要なデータベースアンチパターンを詳細に解説し、その問題点を理解し、解決する方法を解説します!

2. アンチパターンの種類と解説

2.1 ジェイウォーク(信号無視)

このアンチパターンでは、1つの列に複数の値を格納しようとすることで、データの整合性が損なわれます。たとえば、1つの列に複数の都市名をカンマで区切って格納することで、データの更新や検索が困難になります。

  • 集約して表示したいなどの参照用のデータベースであれば余計なクエリをかかなくてすむので仕様によってはありな実装。
--------------------------------------------------------
| 社員ID | 所属部署 |
--------------------------------------------------------
|    1    |  営業    |  
|    2    | 経理  | 
|    3    |  営業/経理   | 
--------------------------------------------------------

このテーブルの所属部署のように、複数のデータを一つの列で管理することをアンチパターンとしています!

このようにして管理しましょう!

--------------------------------------------------------
| 社員ID | 所属部署 |
--------------------------------------------------------
|    1    |  営業    |  
|    2    | 経理  | 
|    3    |  営業   |
|    3    |  経理   | 
--------------------------------------------------------

解決策

  • 各値を個別の行として別々のテーブルに格納する。
  • 関連テーブルを使用して、値の関係性を明確にする。

2.2 ナイーブツリー

ナイーブツリーは、ツリー構造を表現する際に直近の親のみを参照することで構築されます。しかし、この方法ではツリー全体を効果的にクエリすることが難しくなります。

  • こちらも参照用としては、分かりやすいので仕様によってはありな実装。
---------------------------------------
| NodeID | NodeName     | ParentNodeID | AncestorID1 | AncestorID2 | AncestorID3 |
|--------|--------------|--------------|-------------|-------------|-------------|
| 1      | Root         | NULL         | NULL        | NULL        | NULL        |
| 2      | Child 1      | 1            | 1           | NULL        | NULL        |
| 3      | Child 2      | 1            | 1           | NULL        | NULL        |
| 4      | Grandchild 1 | 2            | 2           | 1           | NULL        |
| 5      | Grandchild 2 | 2            | 2           | 1           | NULL        |
| 6      | Grandchild 3 | 3            | 3           | 1           | NULL        |

Root (1)
         /    \
   Child 1 (2) Child 2 (3)
     /   \          \
Grandchild 1 (4) Grandchild 2 (5) Grandchild 3 (6)
  • このように、ツリー形式でデータを管理してしまう状態のことをナイーブツリーという
  • この場合は、データの正合成を保つことが難しくなってしまう。

解決策

  • 各ノードに親や子の参照を追加し、階層を直接表現する。
  • 階層構造を保持するために、上位pathなどの追加情報を使用する。

2.3 IDリクワイアド(とりあえずID)

このアンチパターンでは、複数のIDを組み合わせて主キーを構成することでデータの一意性を保とうとします。しかし、この方法ではデータの整合性が確保されず、システム全体の信頼性が低下します。

  • 使っているフレームワークなどによっては必ず必要になるので、仕様によってこちらも使っても良い。

解決策

  • 自然キーを使用して、データの一意性を確保する。
  • ユニークな識別子やコードを使用して、データの一意性を保持する。

2.4 キーレスエントリ(外部キー嫌い)

キーレスエントリでは、外部キーを使用せずに関連性を表現しようとします。これにより、データベースの参照整合性が損なわれ、データの整合性が保持されなくなります。

  • アプリケーション側で設定していると二重の設定になってしまい、デバックやメンテナンスが難しくなるので、アプリケーションによっては設定しないほうがよい。

解決策

  • 外部キーを適切に使用して、関連性と整合性を確保する。
  • アプリケーション側でのチェックを補完する目的で外部キーを使用する。

2.5 EAV(エンティティ・アトリビュート・バリュー)

EAVモデルは、汎用的な属性テーブルを作成し、各属性の値を横持ちする方法です。この方法では、データの柔軟性が向上しますが、クエリの複雑さやパフォーマンスの低下が懸念されます。

  • 柔軟性が上がっているのでカラムのデータが未知数の場合は、こちらのパターンを使うとよい。
--------------------------------------------------------
| OrderID | 属性名   | 属性値 |
--------------------------------------------------------
|    1    | 品目名   | みかん |
|    2    | 単価     | 100    |
|    3    | カテゴリー | 柑橘類 |
|    4    | 旬       | 冬     |
--------------------------------------------------------

属性値というカラムのdatatypeがバラバラになっているところがダメなところです。


このように改善しましょう!

--------------------------------------------------------
| OrderID | 品目名 | 価格 | カテゴリ | 旬 |
--------------------------------------------------------
|    1    | みかん | 100 | 柑橘類 | 冬 | 
|    2    | レモン | 150 | 柑橘類 | 冬 | 
--------------------------------------------------------

解決策

  • 各属性に専用の列を持つテーブルを作成し、データの構造を明確化する。
  • 汎用的な属性ではなく、固有の属性を個別の列として定義する。

2.6 ポリモーフィック関連

ポリモーフィック関連では、異なるモデルを同じテーブルに統合しようとします。しかし、これによりデータの整合性が損なわれ、参照や制約の設定が困難になります。

  • データの参照が楽になるので、参照用に使うのであればこちらを使うことはよい。

モデルとは?

解決策

  • 各モデルを個別のテーブルに分割し、それぞれのテーブルで関連性を明確化する。
  • 区分を追加して、異なるモデルを区別する。

2.7 マルチカラムアトリビュート(複数列属性)

マルチカラムアトリビュートでは、複数の列に同じ値が重複して格納されます。これにより、データの正規化が妨げられ、更新やクエリの複雑さが増します。

--------------------------------------------------------
| OrderID | 品目名1 | 品目名2 | 品目名3 | 品目名4 |
--------------------------------------------------------
|    1    | みかん | NULL | NULL | NULL | 
|    2    | みかん | りんご | NULL | NULL |
|    3    | いちご | スイカ | NULL | NULL |
|    4    | いちご | もも | メロン | NULL | 
--------------------------------------------------------


このように改善しましょう!

------------------------
| OrderID | 品目名1 
------------------------
|    1    | みかん | 
|    4    | スイカ |
|    3    | りんご |
|    4    | いちご | 
|    5    | もも |
|    5    | メロン | 
-------------------------

解決策

  • 従属テーブルを作成し、関連する値を別々の行として格納する。
  • 正規化を行って、重複したデータを排除する。

2.8 メタデータトリブル(メタデータ大増殖)

過去テーブルを増殖させることでデータを管理しようとします。これにより、データの管理が複雑化し、構造の統一性が損なわれます。
過去テーブルを一年ごとに作成しまうことでこのようなことが起きます。
過去テーブルの管理をルール化しましょう。

解決策

  • 適切なアーキテクチャやパーティショニングを使用して、データの管理を簡素化する。
  • データの適切なバージョン管理を行い、過去テーブルの増加を抑制する。

3. まとめ

ここまで解説してきたようにデータベースアンチパターンは、設計や実装段階での注意が必要です。適切な設計手法やベストプラクティスを適用することで、データベースの効率性や信頼性を向上させることができます。
ですが、DB設計の答えは1つではありません。いくつか書いていたように、アンチパターンというものが存在しているが、絶対に使ってはいけないというものではありません。なぜ、アンチパターンとして存在しているのかが重要で設計の意図がしっかりと説明ができれば使ってもよいのです!

Discussion