@Bill Karwinは、SQL Entity-Attribute-Valueアンチパターンのソリューションを提案する際に、SQLアンチパターンの本で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
また、必須であるはずのサブタイプの属性を強制することもできません。これはアプリケーションで処理する必要がありますが、一般的には理想的ではありません。
具体的なテーブル継承:
継承に取り組む別のアプローチは、サブタイプごとに新しいテーブルを作成し、各テーブルのすべての共通属性を繰り返すことです。例えば:
--// 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
の管理が容易になります。これは、各サブタイプテーブルがポリシーを個別に生成するのではなく、ベーステーブルで処理できるためです。
サブタイプに関係なく、すべてのポリシーを検索するのが非常に簡単になりUNION
ました。必要はありませんSELECT * FROM policies
。
私は、クラステーブルアプローチがほとんどの状況で最も適切であると考えています。
これらの3つのモデルの名前は、MartinFowlerの著書Patternsof EnterpriseApplicationArchitectureに由来しています。