@Bill Karwin описує три моделі успадкування у своїй книзі про антиматеріали SQL , пропонуючи рішення антипаттера SQL Entity-Attribute-Value . Це короткий огляд:
Спадництво однієї таблиці (так само наслідування таблиці за ієрархією):
Використання єдиної таблиці, як у першому варіанті, мабуть, найпростіша конструкція. Як ви вже згадували, багатьом атрибутам, що стосуються підтипу, потрібно буде надати NULL
значення у рядках, де ці атрибути не застосовуються. У цій моделі у вас є одна таблиця політик, яка виглядатиме приблизно так:
+------+---------------------+----------+----------------+------------------+
| 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
атрибуті, як у методі єдиної таблиці. Тип тепер визначається метаданими: ім'ям таблиці.
Однак ця модель також має ряд недоліків:
Загальні атрибути змішуються з атрибутами підтипу, і не існує простого способу їх ідентифікації. База даних також не буде знати.
Визначаючи таблиці, вам доведеться повторити загальні атрибути для кожної таблиці підтипів. Це напевно НЕ СУХО .
Пошук усіх політик незалежно від підтипу стає складним, і знадобиться купа UNION
s.
Ось як слід запитувати всі політики незалежно від типу:
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)
);
Це рішення вирішує проблеми, визначені в двох інших конструкціях:
Обов’язкові атрибути можуть бути застосовані за допомогою NOT NULL
.
Додавання нового підтипу вимагає додавання нової таблиці замість додавання стовпців до існуючої.
Немає ризику встановлення невідповідного атрибуту для певного підтипу.
Немає потреби в type
атрибуті.
Зараз загальні атрибути більше не змішуються з атрибутами підтипу.
Ми можемо залишитися СУХИМ, нарешті. Не потрібно повторювати загальні атрибути для кожної таблиці підтипу під час створення таблиць.
Управління автоматичним збільшенням id
для полісів стає простішим, тому що цим можна керувати базову таблицю, а не кожну таблицю підтипу, генеруючи їх незалежно.
Пошук усіх політик незалежно від підтипу зараз стає дуже простим: не UNION
потрібно - просто a SELECT * FROM policies
.
Я вважаю підхід таблиці класів найбільш підходящим у більшості ситуацій.
Назви цих трьох моделей походять з книги Мартіна Фаулера " Шаблони архітектури прикладних програм підприємства" .