@Bill KarwinอธิบายโมเดลการสืบทอดสามแบบในหนังสือSQL Antipatternsของเขาเมื่อเสนอวิธีแก้ปัญหาให้กับantipattern ของEntity-Attribute-Valueของ SQL นี่คือภาพรวมคร่าวๆ:
การสืบทอดตารางเดี่ยว (aka ตารางต่อการสืบทอดลำดับชั้น):
การใช้ตารางเดียวในตัวเลือกแรกของคุณน่าจะเป็นการออกแบบที่ง่ายที่สุด ดังที่คุณกล่าวถึงคุณลักษณะหลายอย่างที่เป็นประเภทย่อยเฉพาะจะต้องได้รับ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
มาก
นี่คือวิธีที่คุณจะต้องค้นหานโยบายทั้งหมดโดยไม่คำนึงถึงประเภท:
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
สำหรับแต่ละประเภทย่อย สิ่งนี้สามารถนำไปสู่ข้อบกพร่องในแอปพลิเคชันของคุณได้อย่างง่ายดายหากการดำเนินการนี้ถูกลืม
การสืบทอดตารางคลาส (aka ตารางต่อการสืบทอดประเภท):
นี้เป็นวิธีการที่@ David กล่าวถึงในคำตอบอื่น คุณสร้างตารางเดียวสำหรับคลาสพื้นฐานของคุณซึ่งรวมถึงแอตทริบิวต์ทั่วไปทั้งหมด จากนั้นคุณจะสร้างตารางเฉพาะสำหรับแต่ละประเภทย่อยซึ่งมีคีย์หลักทำหน้าที่เป็นforeign keyไปยังตารางฐาน ตัวอย่าง:
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
s จำเป็น - SELECT * FROM policies
เพียง
ฉันคิดว่าวิธีตารางเรียนเป็นวิธีที่เหมาะสมที่สุดในสถานการณ์ส่วนใหญ่
ชื่อของทั้งสามรุ่นมาจากมาร์ตินฟาวเลอร์ของหนังสือรูปแบบของสถาปัตยกรรม Enterprise Application