第三個選項是創(chuàng)建一個“Policy”表,然后創(chuàng)建一個“SectionsMain”表,用于存儲跨不同類型的部分所共有的所有字段。然后為每種類型的部分創(chuàng)建其他表,僅包含不常見的字段。
決定哪個最好主要取決于您有多少字段以及您想要如何編寫 SQL。他們都會工作。如果你只有幾個字段,那么我可能會選擇#1。對于“很多”領域,我傾向于#2 或#3。
@Bill Karwin 在他的 SQL Antipatterns 書,在提出 SQL 實體屬性值反模式。這是一個簡短的概述:
像第一個選項一樣使用單個表可能是最簡單的設計。正如您所提到的,許多特定于子類型的屬性必須在這些屬性不適用的行上被賦予 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 -----/
保持設計簡單是一個優(yōu)點,但這種方法的主要問題如下:
在添加新的子類型時,您必須更改表以適應描述這些新對象的屬性。當您有許多子類型或者您計劃定期添加子類型時,這很快就會成為問題。
數(shù)據(jù)庫將無法強制執(zhí)行哪些屬性適用,哪些不適用,因為沒有元數(shù)據(jù)來定義哪些屬性屬于哪些子類型。
您也無法對本應強制執(zhí)行的子類型屬性強制執(zhí)行 NOT NULL
。您必須在應用程序中處理這個問題,這通常并不理想。
解決繼承問題的另一種方法是為每個子類型創(chuàng)建一個新表,重復每個表中的所有公共屬性。例如:
--// 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 | +------+---------------------+------------------+
這種設計將基本上解決單表方法所確定的問題:
現(xiàn)在可以通過 NOT NULL
強制執(zhí)行強制屬性。
添加新子類型需要添加新表,而不是向現(xiàn)有表添加列。
也不存在為特定子類型設置不適當屬性的風險,例如屬性策略的 vehicle_reg_no
字段。
不需要像單表方法中那樣的 type
屬性。該類型現(xiàn)在由元數(shù)據(jù)定義:表名稱。
但是這種模型也有一些缺點:
公共屬性與子類型特定屬性混合在一起,沒有簡單的方法來識別它們。數(shù)據(jù)庫也不知道。
定義表時,您必須為每個子類型表重復公共屬性。這絕對不是干。
無論子類型如何,搜索所有策略都變得很困難,并且需要一堆 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
來修改上述查詢。如果忘記此操作,很容易導致應用程序出現(xiàn)錯誤。
這是@David 在中提到的解決方案另一個答案。您為基類創(chuàng)建一個表,其中包括所有公共屬性。然后,您將為每個子類型創(chuàng)建特定的表,其主鍵也充當基表。示例:
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) );
該解決方案解決了其他兩種設計中發(fā)現(xiàn)的問題:
可以通過 NOT NULL
強制執(zhí)行強制屬性。
添加新子類型需要添加新表,而不是向現(xiàn)有表添加列。
沒有為特定子類型設置不適當屬性的風險。
不需要 type
屬性。
現(xiàn)在公共屬性不再與子類型特定屬性混合。
我們終于可以保持干燥了。創(chuàng)建表時無需重復每個子類型表的公共屬性。
管理策略的自動遞增 id
變得更加容易,因為這可以由基表處理,而不是每個子類型表獨立生成它們。
搜索所有策略(無論子類型如何)現(xiàn)在都變得非常容易:不需要 UNION
- 只需 SELECT * FROM 策略
。
我認為類表方法在大多數(shù)情況下是最合適的。
這三個模型的名稱來自Martin Fowler一本書企業(yè)應用架構(gòu)模式。