@Bill Karwin menjelaskan tiga model pewarisan dalam buku SQL Antipatterns-nya, ketika mengusulkan solusi untuk antipattern SQL Entity-Attribute-Value. Ini adalah gambaran singkatnya:
Pewarisan Tabel Tunggal (alias Pewarisan Tabel Per Hierarki):
Menggunakan satu tabel seperti pada opsi pertama Anda mungkin merupakan desain yang paling sederhana. Seperti yang Anda sebutkan, banyak atribut yang spesifik untuk subtipe harus diberi NULL
nilai pada baris di mana atribut ini tidak berlaku. Dengan model ini, Anda akan memiliki satu tabel kebijakan, yang akan terlihat seperti ini:
+------+---------------------+----------+----------------+------------------+
| 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 -----/
Menjaga desain tetap sederhana adalah nilai tambah, tetapi masalah utama dengan pendekatan ini adalah sebagai berikut:
-
Saat menambahkan subtipe baru, Anda harus mengubah tabel untuk mengakomodasi atribut yang menjelaskan objek baru ini. Ini dapat dengan cepat menjadi masalah jika Anda memiliki banyak subtipe, atau jika Anda berencana untuk menambahkan subtipe secara teratur.
-
Basis data tidak akan dapat menerapkan atribut mana yang berlaku dan mana yang tidak, karena tidak ada metadata untuk menentukan atribut mana yang termasuk dalam subtipe mana.
-
Anda juga tidak dapat menerapkan
NOT NULL
pada atribut subtipe yang harus wajib. Anda harus menangani ini di aplikasi Anda, yang secara umum tidak ideal.
Warisan Meja Beton:
Pendekatan lain untuk mengatasi pewarisan adalah dengan membuat tabel baru untuk setiap subtipe, mengulangi semua atribut umum di setiap tabel. Misalnya:
--// 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 |
+------+---------------------+------------------+
Desain ini pada dasarnya akan memecahkan masalah yang diidentifikasi untuk metode tabel tunggal:
-
Atribut wajib sekarang dapat diterapkan dengan
NOT NULL
. -
Menambahkan subtipe baru memerlukan penambahan tabel baru alih-alih menambahkan kolom ke yang sudah ada.
-
Juga tidak ada risiko bahwa atribut yang tidak pantas disetel untuk subtipe tertentu, seperti
vehicle_reg_no
bidang untuk kebijakan properti. -
Tidak perlu
type
atribut seperti dalam metode tabel tunggal. Jenisnya sekarang ditentukan oleh metadata:nama tabel.
Namun model ini juga memiliki beberapa kelemahan:
-
Atribut umum dicampur dengan atribut khusus subtipe, dan tidak ada cara mudah untuk mengidentifikasinya. Basis data juga tidak akan tahu.
-
Saat mendefinisikan tabel, Anda harus mengulangi atribut umum untuk setiap tabel subtipe. Itu jelas bukan KERING.
-
Mencari semua kebijakan terlepas dari subtipe menjadi sulit, dan akan membutuhkan banyak
UNION
s.
Ini adalah bagaimana Anda harus menanyakan semua kebijakan terlepas dari jenisnya:
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;
Perhatikan bagaimana menambahkan subtipe baru akan memerlukan permintaan di atas untuk dimodifikasi dengan tambahan UNION ALL
untuk setiap subtipe. Ini dapat dengan mudah menyebabkan bug di aplikasi Anda jika operasi ini dilupakan.
Pewarisan Tabel Kelas (alias Warisan Tabel Per Jenis):
Ini adalah solusi yang @David sebutkan di jawaban lain. Anda membuat satu tabel untuk kelas dasar Anda, yang mencakup semua atribut umum. Kemudian Anda akan membuat tabel khusus untuk setiap subtipe, yang kunci utamanya juga berfungsi sebagai kunci asing ke tabel dasar. Contoh:
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)
);
Solusi ini memecahkan masalah yang diidentifikasi dalam dua desain lainnya:
-
Atribut wajib dapat diterapkan dengan
NOT NULL
. -
Menambahkan subtipe baru memerlukan penambahan tabel baru alih-alih menambahkan kolom ke yang sudah ada.
-
Tidak ada risiko bahwa atribut yang tidak pantas disetel untuk subtipe tertentu.
-
Tidak perlu
type
atribut. -
Sekarang atribut umum tidak lagi dicampur dengan atribut khusus subtipe.
-
Kita bisa tetap KERING, akhirnya. Tidak perlu mengulang atribut umum untuk setiap tabel subtipe saat membuat tabel.
-
Mengelola
id
yang bertambah secara otomatis untuk kebijakan menjadi lebih mudah, karena ini dapat ditangani oleh tabel dasar, daripada setiap tabel subtipe yang membuatnya secara mandiri. -
Mencari semua kebijakan terlepas dari subtipe sekarang menjadi sangat mudah:Tidak ada
UNION
s diperlukan - hanyaSELECT * FROM policies
.
Saya menganggap pendekatan tabel kelas sebagai yang paling cocok di sebagian besar situasi.
Nama ketiga model ini berasal dari buku Martin Fowler Patterns of Enterprise Application Architecture.