22

假設您設置了一個數據庫來存儲各種車輛的碰撞測試數據。你想存儲快艇,汽車和卡丁車碰撞測試的數據。數據庫設計中的繼承問題

您可以創建三個獨立的表格:SpeedboatTests,CarTests和GokartTests。但是很多列在每個表中都是相同的(例如,執行測試的人的員工ID,碰撞方向(前,側,後)等)。然而,大量的列會有所不同,所以你不想只把所有的測試數據放在一張表中,因爲你會有很多列對於快艇總是空的,但是有很多列會一直存在對於汽車來說是空的,並且對於卡丁車來說,其中的很少一些將是空的。

假設您還想存儲一些與測試沒有直接關係的信息(例如正在測試的東西的設計者的員工ID)。這些專欄似乎根本不適合放入「測試」表格,特別是因爲它們會在同一輛車上的所有測試中重複使用。

讓我來舉例說明表格的一種可能的排列方式,以便您可以看到所涉及的問題。

 
Speedboats 
id | col_about_speedboats_but_not_tests1 | col_about_speedboats_but_not_tests2 

Cars 
id | col_about_cars_but_not_tests1 | col_about_cars_but_not_tests2 

Gokarts 
id | col_about_gokarts_but_not_tests1 | col_about_gokarts_but_not_tests2 

Tests 
id | type | id_in_type | col_about_all_tests1 | col_about_all_tests2 
(id_in_type will refer to the id column of one of the next three tables, 
depending on the value of type) 

SpeedboatTests 
id | speedboat_id | col_about_speedboat_tests1 | col_about_speedboat_tests2 

CarTests 
id | car_id | col_about_car_tests1 | col_about_car_tests2 

GokartTests 
id | gokart_id | col_about_gokart_tests1 | col_about_gokart_tests2 

什麼是這種結構的好/壞,以及實施類似這樣的東西的首選方式是什麼?

如果還有一些信息適用於您希望在車輛表中使用的所有車輛,該怎麼辦?那麼CarTests表會看起來像這樣...

 
id | vehicle_id | ... 

With a Vehicles table like this: 
id | type | id_in_type 
(with id_in_type pointing to the id of either a speedboat, car, or go-kart) 

這似乎是一個皇家混亂似乎。應該如何設置這樣的東西?

+0

可能的重複[如何有效地建模數據庫中的繼承?](http://stackoverflow.com/questions/190296/how-do-you-effectively-model-inheritance-in-a-database) – Musa 2014-08-25 13:45:23

回答

37

typeid_in_type設計被稱爲Polymorphic Associations。這種設計以多種方式破壞了規範化的規則。如果不出意外,這應該是一個紅旗,你不能聲明一個真正的外鍵約束,因爲id_in_type可以引用任何數表。

這裏的定義你的表的一個更好的辦法:

  • 做一個抽象的表Vehicles提供所有車輛的子類型和車輛測試一個抽象的參考點。
  • 每輛車子類型具有不自動遞增主鍵,而是引用Vehicles
  • 每個測試子類型都有一個不能自動遞增的主鍵,而是引用Tests
  • 每個測試子類型也有相應車輛子類型的外鍵。

這裏的樣本DDL:

CREATE TABLE Vehicles (
vehicle_id INT AUTO_INCREMENT PRIMARY KEY 
); 

CREATE TABLE Speedboats (
vehicle_id INT PRIMARY KEY, 
col_about_speedboats_but_not_tests1 INT, 
col_about_speedboats_but_not_tests2 INT, 
FOREIGN KEY(vehicle_id) REFERENCES Vehicles(vehicle_id) 
); 

CREATE TABLE Cars (
vehicle_id INT PRIMARY KEY, 
col_about_cars_but_not_tests1 INT, 
col_about_cars_but_not_tests2 INT, 
FOREIGN KEY(vehicle_id) REFERENCES Vehicles(vehicle_id) 
); 

CREATE TABLE Gokarts (
vehicle_id INT PRIMARY KEY, 
col_about_gokarts_but_not_tests1 INT, 
col_about_gokarts_but_not_tests2 INT, 
FOREIGN KEY(vehicle_id) REFERENCES Vehicles(vehicle_id) 
); 

CREATE TABLE Tests (
test_id INT AUTO_INCREMENT PRIMARY KEY, 
col_about_all_tests1 INT, 
col_about_all_tests2 INT 
); 

CREATE TABLE SpeedboatTests (
test_id INT PRIMARY KEY, 
vehicle_id INT NOT NULL, 
col_about_speedboat_tests1 INT, 
col_about_speedboat_tests2 INT, 
FOREIGN KEY(test_id) REFERENCES Tests(test_id), 
FOREIGN KEY(vehicle_id) REFERENCES Speedboats(vehicle_id) 
); 

CREATE TABLE CarTests (
test_id INT PRIMARY KEY, 
vehicle_id INT NOT NULL, 
col_about_car_tests1 INT, 
col_about_car_tests2 INT, 
FOREIGN KEY(test_id) REFERENCES Tests(test_id), 
FOREIGN KEY(vehicle_id) REFERENCES Cars(vehicle_id) 
); 

CREATE TABLE GokartTests (
test_id INT PRIMARY KEY, 
vehicle_id INT NOT NULL, 
col_about_gokart_tests1 INT, 
col_about_gokart_tests2 INT, 
FOREIGN KEY(test_id) REFERENCES Tests(test_id), 
FOREIGN KEY(vehicle_id) REFERENCES Gokarts(vehicle_id) 
); 

你可以或者聲明Tests.vehicle_id它引用Vehicles.vehicle_id,擺脫在每個測試分型表vehicle_id外鍵的,但將允許異常,如快艇測試引用了gokart的id。

0

我會把它分成不同的表格,例如:車輛(ID,類型等)VehicleAttributes()VehicleID,AttributeID,Value),CrashTestInfo(VehicleID,CrashtestID,Date等)CrashtestAttributes(CrashTestID,AttributeID,Value)

或者不是屬性,應記錄類似的細節。

+0

這是實體 - 屬性 - 價值設計,這對於OP的情景來說是過度的。 – 2009-02-16 21:44:56

14

對於將繼承層次結構映射到數據庫表,我認爲Martin Fowler在他的「企業應用程序體系結構模式」一書中列出了相當好的選擇。

http://martinfowler.com/eaaCatalog/singleTableInheritance.html

http://martinfowler.com/eaaCatalog/classTableInheritance.html

http://martinfowler.com/eaaCatalog/concreteTableInheritance.html

如果附加字段/列的數目是很小的子類,然後單表繼承通常是最簡單的處理。

如果你正在使用PostgreSQL爲你的數據庫和你願不願意把自己綁在一個特定的數據庫功能,它支持直接表繼承:

http://www.postgresql.org/docs/8.3/static/ddl-inherit.html

+0

我想補充一點,具體參考原始問題中提到的皇室混亂情況,即外鍵將從特定車型指向抽象車輛表。即speedboat(vehicle_id FK,speedboat_specific_column1等) – Robin 2009-02-16 21:36:48

-3

你的設計是合理的,是繼正確的規範化規則。你可能會遺漏一個帶有車輛標識和類型的車輛表(例如,快艇,汽車和Gokarts的「父母」......你可以在其中保存諸如「DesignedByUserId」之類的東西)。車輛表和快艇之間是一對一的關係,車與快艇/汽車/ GoKarts之間有一對一的關係(即一輛車只能有一條快艇記錄,汽車或卡丁車)...雖然大多數數據庫並沒有提供一個簡單的執法機制。

一個規範化規則,可幫助確定這些事情是一個字段應該只在表的主鍵依賴。在將快艇,汽車和gokart測試結果存儲在一起的統一表格中,汽車相關領域不僅取決於測試日期,還取決於車輛ID和車輛類型。測試結果表的主要關鍵是測試日期+車輛ID,車輛類型不是使得測試數據行具有唯一性的因素(也就是說,在某一特定車輛上是否有在01/01/200912:30進行的測試那既是快艇又是汽車......不行......不能這樣做)。

我不解釋規範化規則particularily很好......但3/4/5的正常形式的規則總是混淆了我,當我讀到的正式描述。其中一個(3rd/4th/5th)根據主鍵和僅主鍵處理字段。該規則假定主鍵已被正確識別(錯誤地定義主鍵太容易)。

+1

-1,因爲多態關聯設計(`type`和`id_in_type`事物)不是標準化設計。 – 2009-02-16 21:28:54

+0

呃......見http://en.wikipedia.org/wiki/Fourth_normal_form。比薩的例子是相當合理的。 – user53794 2009-02-16 22:13:24

+1

你說{test_id,type} - > - > {id_in_type}傳遞4NF,因此{test_id,type}是一個超級鍵嗎?我在談論一個關係的基本定義,其中每個屬性表示一個「事物」的值 - 但id_in_type是三種不同的事物。 – 2009-02-17 01:41:44

0

做一個谷歌搜索「gen-spec關係建模」。您將找到關於如何設置存儲廣義實體屬性(OO程序員可能稱爲超類)的表的表單,關於每個專用實體(子類)的單獨表以及如何使用外鍵來鏈接它全部一起。

最好的文章,國際海事組織,討論ER建模方面根規格。如果您知道如何將ER模型轉換爲關係模型,然後再轉換爲SQL表格,那麼一旦他們向您展示如何在ER中建模gen-spec,您就會知道該怎麼做。

如果你只是谷歌的「GEN-規範」,大多數的什麼,你會看到的是面向對象的,而不是面向關係。只要你知道如何克服對象關係阻抗不匹配,這些東西也可能是有用的。

0

如果您使用SQLAlchemy,Python的對象關係映射器,您可以使用configure how inheritance hierarchies are mapped to database tables。對象關係映射器對馴服繁瑣的SQL非常有用。

您的問題可能很適合垂直表格。不是將所有內容都存儲在模式中,而是將對象的類型和主鍵存儲在一個表中,並將鍵/值元組存儲在另一個表中的每個對象中。如果您確實在存儲汽車測試,則此設置將使添加新類型結果變得更容易。