@Bill Karwin mô tả ba mô hình kế thừa trong cuốn sách Phản vật chất SQL của mình, khi đề xuất giải pháp cho phản vật chất-Thuộc tính-Giá trị SQL. Đây là tổng quan ngắn gọn:
Kế thừa một bảng (hay còn gọi là Kế thừa theo hệ thống theo bảng):
Sử dụng một bảng đơn như trong tùy chọn đầu tiên của bạn có lẽ là thiết kế đơn giản nhất. Như bạn đã đề cập, nhiều thuộc tính dành riêng cho kiểu phụ sẽ phải được cung cấp NULL
giá trị trên các hàng không áp dụng các thuộc tính này. Với mô hình này, bạn sẽ có một bảng chính sách, trông giống như sau:
+------+---------------------+----------+----------------+------------------+
| 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 -----/
Giữ cho thiết kế đơn giản là một lợi thế, nhưng các vấn đề chính của cách tiếp cận này là:
-
Khi nói đến việc thêm các kiểu phụ mới, bạn sẽ phải thay đổi bảng để phù hợp với các thuộc tính mô tả các đối tượng mới này. Điều này có thể nhanh chóng trở thành vấn đề khi bạn có nhiều kiểu con hoặc nếu bạn định thêm kiểu con thường xuyên.
-
Cơ sở dữ liệu sẽ không thể thực thi thuộc tính nào áp dụng và thuộc tính nào không áp dụng, vì không có siêu dữ liệu để xác định thuộc tính nào thuộc về kiểu con nào.
-
Bạn cũng không thể thực thi
NOT NULL
trên các thuộc tính của một kiểu con cần phải bắt buộc. Bạn sẽ phải xử lý vấn đề này trong ứng dụng của mình, điều này nói chung không phải là lý tưởng.
Kế thừa Bảng Bê tông:
Một cách tiếp cận khác để giải quyết sự kế thừa là tạo một bảng mới cho mỗi kiểu con, lặp lại tất cả các thuộc tính chung trong mỗi bảng. Ví dụ:
--// 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 |
+------+---------------------+------------------+
Thiết kế này về cơ bản sẽ giải quyết các vấn đề được xác định cho phương pháp bảng đơn:
-
Các thuộc tính bắt buộc hiện có thể được thực thi với
NOT NULL
. -
Việc thêm một kiểu phụ mới yêu cầu phải thêm một bảng mới thay vì thêm các cột vào một bảng hiện có.
-
Cũng không có rủi ro rằng một thuộc tính không phù hợp được đặt cho một loại phụ cụ thể, chẳng hạn như
vehicle_reg_no
trường cho chính sách tài sản. -
Không cần loại
type
thuộc tính như trong phương pháp bảng đơn. Loại hiện được xác định bởi siêu dữ liệu:tên bảng.
Tuy nhiên, mô hình này cũng đi kèm với một số nhược điểm:
-
Các thuộc tính chung được trộn lẫn với các thuộc tính cụ thể của loại phụ và không có cách nào dễ dàng để xác định chúng. Cơ sở dữ liệu cũng sẽ không biết.
-
Khi xác định các bảng, bạn sẽ phải lặp lại các thuộc tính chung cho mỗi bảng kiểu con. Đó chắc chắn không phải là KHÔ.
-
Việc tìm kiếm tất cả các chính sách bất kể loại phụ trở nên khó khăn và sẽ yêu cầu một loạt
UNION
s.
Đây là cách bạn sẽ phải truy vấn tất cả các chính sách bất kể loại:
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;
Lưu ý rằng việc thêm các kiểu phụ mới sẽ yêu cầu sửa đổi truy vấn trên bằng một UNION ALL
bổ sung như thế nào cho mỗi loại con. Điều này có thể dễ dàng dẫn đến lỗi trong ứng dụng của bạn nếu bạn quên thao tác này.
Kế thừa bảng lớp (hay còn gọi là kế thừa bảng mỗi loại):
Đây là giải pháp mà @David đề cập trong câu trả lời khác. Bạn tạo một bảng duy nhất cho lớp cơ sở của mình, bảng này bao gồm tất cả các thuộc tính chung. Sau đó, bạn sẽ tạo các bảng cụ thể cho từng kiểu con, mà khóa chính của chúng cũng đóng vai trò là khóa ngoại cho bảng cơ sở. Ví dụ:
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)
);
Giải pháp này giải quyết các vấn đề được xác định trong hai thiết kế còn lại:
-
Các thuộc tính bắt buộc có thể được thực thi với
NOT NULL
. -
Việc thêm một kiểu phụ mới yêu cầu phải thêm một bảng mới thay vì thêm các cột vào một bảng hiện có.
-
Không có rủi ro rằng một thuộc tính không phù hợp được đặt cho một loại phụ cụ thể.
-
Không cần loại
type
thuộc tính. -
Giờ đây, các thuộc tính chung không bị trộn lẫn với các thuộc tính cụ thể của loại phụ nữa.
-
Cuối cùng thì chúng ta cũng có thể ở DRY. Không cần phải lặp lại các thuộc tính chung cho mỗi bảng kiểu con khi tạo bảng.
-
Quản lý
id
tăng dần tự động đối với các chính sách trở nên dễ dàng hơn, vì điều này có thể được xử lý bởi bảng cơ sở, thay vì mỗi bảng loại phụ tạo ra chúng một cách độc lập. -
Việc tìm kiếm tất cả các chính sách bất kể loại phụ giờ đây trở nên rất dễ dàng:Không có
UNION
cần thiết - chỉ cần mộtSELECT * FROM policies
.
Tôi coi cách tiếp cận bảng lớp là phù hợp nhất trong hầu hết các tình huống.
Tên của ba mô hình này xuất phát từ cuốn sách Các mẫu kiến trúc ứng dụng doanh nghiệp của Martin Fowler.