@Bill Karwinは、SQL Entity-Attribute-Valueアンチパターンのソリューションを提案するときに、SQL Antipatternsブックで3つの継承モデルについて説明しています。これは簡単な概要です:
単一のテーブルの継承(別名、階層ごとのテーブルの継承):
最初のオプションのように単一のテーブルを使用することは、おそらく最も単純な設計です。すでに述べたように、サブタイプ固有の多くの属性にはNULL
、これらの属性が適用されない行の値を指定する必要があります。このモデルでは、次のようなポリシーテーブルが1つあります。
+------+---------------------+----------+----------------+------------------+
| id | date_issued | type | vehicle_reg_no | property_address |
+------+---------------------+----------+----------------+------------------+
| 1 | 2010-08-20 12:00:00 | MOTOR | 01-A-04004 | NULL |
| 2 | 2010-08-20 13:00:00 | MOTOR | 02-B-01010 | NULL |
| 3 | 2010-08-20 14:00:00 | PROPERTY | NULL | Oxford Street |
| 4 | 2010-08-20 15:00:00 | MOTOR | 03-C-02020 | NULL |
+------+---------------------+----------+----------------+------------------+
\------ COMMON FIELDS -------/ \----- SUBTYPE SPECIFIC FIELDS -----/
設計をシンプルに保つことはプラスですが、このアプローチの主な問題は次のとおりです。
新しいサブタイプを追加する場合、これらの新しいオブジェクトを説明する属性に対応するようにテーブルを変更する必要があります。これは、多くのサブタイプがある場合、または定期的にサブタイプを追加する予定がある場合、すぐに問題になる可能性があります。
どの属性がどのサブタイプに属しているかを定義するメタデータがないため、データベースは適用する属性と適用しない属性を強制できません。
またNOT NULL
、必須であるべきサブタイプの属性を強制することもできません。アプリケーションでこれを処理する必要がありますが、これは一般的に理想的ではありません。
具体的なテーブルの継承:
継承に取り組むもう1つの方法は、サブタイプごとに新しいテーブルを作成し、各テーブルのすべての共通属性を繰り返すことです。例えば:
--// Table: policies_motor
+------+---------------------+----------------+
| id | date_issued | vehicle_reg_no |
+------+---------------------+----------------+
| 1 | 2010-08-20 12:00:00 | 01-A-04004 |
| 2 | 2010-08-20 13:00:00 | 02-B-01010 |
| 3 | 2010-08-20 15:00:00 | 03-C-02020 |
+------+---------------------+----------------+
--// Table: policies_property
+------+---------------------+------------------+
| id | date_issued | property_address |
+------+---------------------+------------------+
| 1 | 2010-08-20 14:00:00 | Oxford Street |
+------+---------------------+------------------+
この設計は基本的に、単一テーブル法で特定された問題を解決します。
必須属性をで適用できるようになりましたNOT NULL
。
新しいサブタイプを追加するには、既存のテーブルに列を追加するのではなく、新しいテーブルを追加する必要があります。
vehicle_reg_no
プロパティポリシーのフィールドなど、特定のサブタイプに不適切な属性が設定されるリスクもありません。
type
シングルテーブルメソッドのように属性は必要ありません。タイプはメタデータ(テーブル名)によって定義されます。
ただし、このモデルにはいくつかの欠点もあります。
共通の属性はサブタイプ固有の属性と混合されており、それらを識別する簡単な方法はありません。データベースはどちらも知りません。
テーブルを定義するときは、サブタイプテーブルごとに共通の属性を繰り返す必要があります。それは間違いなくDRYではありません。
サブタイプに関係なくすべてのポリシーを検索することは困難になり、大量のが必要になりますUNION
。
これは、タイプに関係なくすべてのポリシーを照会する方法です。
SELECT date_issued, other_common_fields, 'MOTOR' AS type
FROM policies_motor
UNION ALL
SELECT date_issued, other_common_fields, 'PROPERTY' AS type
FROM policies_property;
新しいサブタイプを追加するには、上記のクエリをUNION ALL
サブタイプごとに追加して変更する必要があることに注意してください。この操作を忘れると、アプリケーションのバグが発生しやすくなります。
クラステーブルの継承(型ごとのテーブルの別名):
これは、@ Davidが他の回答で言及しているソリューションです。基本クラスの単一のテーブルを作成します。これには、すべての共通属性が含まれています。次に、サブタイプごとに特定のテーブルを作成します。その主キーは、ベーステーブルへの外部キーとしても機能します。例:
CREATE TABLE policies (
policy_id int,
date_issued datetime,
-- // other common attributes ...
);
CREATE TABLE policy_motor (
policy_id int,
vehicle_reg_no varchar(20),
-- // other attributes specific to motor insurance ...
FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);
CREATE TABLE policy_property (
policy_id int,
property_address varchar(20),
-- // other attributes specific to property insurance ...
FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);
このソリューションは、他の2つの設計で特定された問題を解決します。
必須属性はを使用して適用できますNOT NULL
。
新しいサブタイプを追加するには、既存のテーブルに列を追加するのではなく、新しいテーブルを追加する必要があります。
特定のサブタイプに不適切な属性が設定されるリスクはありません。
type
属性は必要ありません。
これで、共通の属性はサブタイプ固有の属性と混合されなくなりました。
やっとドライになりました。テーブルを作成するときに、サブタイプテーブルごとに共通の属性を繰り返す必要はありません。
id
ポリシーの自動インクリメントの管理は、サブタイプテーブルごとに個別に生成するのではなく、ベーステーブルで処理できるため、より簡単になります。
サブタイプに関係なくすべてのポリシーを検索することが非常に簡単になりました。sはUNION
必要ありませんSELECT * FROM policies
。
クラステーブルアプローチは、ほとんどの状況で最も適切であると考えています。
これら3つのモデルの名前は、Martin Fowlerの著書「Patterns of Enterprise Application Architecture」に由来しています。