2009-11-13 42 views
11

在數據庫中模擬Tagged union的最佳方法是什麼? 我說的是這樣的:如何模擬數據庫中的標記聯合?

create table t1 { 
    vehicle_id INTEGER NOT NULL REFERENCES car(id) OR motor(id) -- not valid 
    ... 
} 

其中vehicle_id將汽車表或電機表ID,它會知道。

(假設電機和車載臺也沒什麼common0

回答

9

有些人使用一種叫做多態關聯做這個設計,讓vehicle_id包含存在無論是在carmotor表的值,然後添加一個vehicle_type,它命名t1中給定的行的表

問題是,如果你這樣做,你不能聲明一個真正的SQL外鍵約束在SQL中沒有支持的外鍵含多處參考目標。還有其他問題,但缺乏參照完整性已經成爲一種破壞行爲。

更好的設計是從兩個carmotor常見的超的面向對象的設計借用一個概念:

CREATE TABLE Identifiable (
id SERIAL PRIMARY KEY 
); 

然後讓t1參考此超表:

CREATE TABLE t1 (
    vehicle_id INTEGER NOT NULL, 
    FOREIGN KEY (vehicle_id) REFERENCES identifiable(id) 
    ... 
); 

還使子類型引用其父超類型。請注意,子類型的主鍵是而不是自動遞增。父超類型負責分配一個新的id值,而孩子只引用該值。

CREATE TABLE car (
    id INTEGER NOT NULL, 
    FOREIGN KEY (id) REFERENCES identifiable(id) 
    ... 
); 

CREATE TABLE motor (
    id INTEGER NOT NULL, 
    FOREIGN KEY (id) REFERENCES identifiable(id) 
    ... 
); 

現在你可以有真正的參照完整性,而且還支持多種亞型表有自己的屬性。


通過@Quassnoi答案還示出了執行不相交亞型的方法。也就是說,要防止carmotor引用其父超類型表中的同一行。當我這樣做時,我使用單列主鍵Identifiable.id,但也通過Identifiable.(id, type)聲明UNIQUE鍵。 carmotor中的外鍵可以引用兩列唯一鍵而不是主鍵。

+0

「可識別」的代理鍵只有在查詢需要選擇的「可識別」屬性中才有用。如果「可識別」僅用於強制執行約束,則使用複合密鑰將允許在查詢中競爭地擺脫它。 – Quassnoi 2009-11-13 17:54:10

+1

我想出了自己使用的'共同超類型'方法,並在主要的系統遷移/重建項目中成功地使用了它。 (新西蘭政府,教育部SPOT25) – 2012-06-26 22:52:01

5
CREATE TABLE vehicle (type INT NOT NULL, id INT NOT NULL, 
      PRIMARY KEY (type, id) 
) 

CREATE TABLE car (type INT NOT NULL DEFAULT 1, id INT NOT NULL PRIMARY KEY, 
      CHECK(type = 1), 
      FOREIGN KEY (type, id) REFERENCES vehicle 
) 

CREATE TABLE motorcycle (type INT NOT NULL DEFAULT 2, id INT NOT NULL PRIMARY KEY, 
      CHECK(type = 2), 
      FOREIGN KEY (type, id) REFERENCES vehicle 
) 

CREATE TABLE t1 (
    ... 
    vehicle_type INT NOT NULL, 
    vehicle_id INT NOT NULL, 
    FOREIGN KEY (vehicle_type, vehicle_id) REFERENCES vehicle 
    ... 
) 
+0

它會做如果將'VEHICLE.VEHICLE_ID'定義爲主鍵,那麼生活就會更簡單,因此您不必引用組合鍵,並使用唯一約束來鍵入&id列。 – 2009-11-13 17:36:43

+0

'@OMG小馬:'有了這個佈局,你根本不需要參考'車輛'。你可以加入'汽車'或'摩托車',這取決於'類型'。這裏的「車輛」只是爲了警告關係。 – Quassnoi 2009-11-13 17:50:50

+0

用這種方法,有沒有辦法保證在「汽車」或「摩托車」中沒有相應的行的「孤兒」汽車? – 2017-09-25 21:31:23

3

我想你可以使用table inheritance in PostgreSQL來建模這樣的參考。

如果你真的需要知道在哪裏一行來自於查詢,你可以使用一個簡單的UNION ALL statment像(這種可能性有沒有關係表繼承):

SELECT car.*, 'car' table_name 
UNION ALL 
SELECT motor.*, 'motor' table_name