2011-07-31 67 views
1

我想建立一個多對多的關係,約束條件是任何時候只能關聯關係每一方的一個實體或沒有實體。在關係數據庫中表示受限多對多關係的最佳方式是什麼?

這個問題的一個很好的比喻是汽車和停車庫空間。有許多汽車和許多空間。一個空間可以包含一輛車或是空的;一輛汽車一次只能在一個空間內,或者沒有空間(不停放)。

我們有一個Cars表和一個Spaces表(可能還有一個鏈接表)。汽車表中的每一行代表一輛汽車的唯一實例(包括許可證,車主,型號等),而空格表中的每一行代表一個獨特的停車位(車庫地板級別,行號和編號)。在數據庫中鏈接這些表並強制執行上述約束的最佳方法是什麼?

(我正在使用C#,NHibernate和Oracle。)

回答

2

如果你不擔心的歷史 - 即只擔心「現在」,這樣做:

create table parking (
    car_id references car, 
    space_id references space, 
    unique car_id, 
    unique space_id 
); 

使兩個汽車和空間參考獨一無二的,你限制每一方最多一個鏈接 - 即一輛汽車可以停放在最多一個空間內,而一個空間最多可以停放一輛汽車。

+0

這是有道理的 - 每個外鍵的唯一約束將強制執行我後面的約束。 – CyberMonk

0

創建第三個表。

parking 
-------- 
car_id 
space_id 
start_dt 
end_dt 

對於約束,我想你的情況的問題是,你需要檢查交叉表本身的複雜規則。如果您在觸發器中嘗試此操作,則會報告突變。

避免這種情況的一種方法是複製表,並針對此約束查詢此複製。

+0

謝謝,我傾向於一個鏈接表。不知道我是否理解如何應用複製,但。這將如何執行約束?另外,我並不關心我的情況下的時間 - 只是車庫的現狀。 – CyberMonk

1

在任何關係數據庫中,多對多關係都必須有一個連接表來表示組合。正如答案中所提供的(但沒有太多的理論背景),如果沒有中間的表來存儲所有組合,則不能表示多對多的關係。

在解決方案中還提到,如果您不需要歷史記錄,它只能解決您的問題。當我告訴你真實世界的應用程序幾乎總是需要表示歷史數據時,請相信我。有很多方法可以做到這一點,但一個簡單的方法可能是創建所謂的與另一個表的三元關係。理論上講,您可以創建一個「時間」表,該表還將其主鍵(如明確的時間戳)與其他兩個源表的繼承關鍵字鏈接起來。這樣可以防止在同一時間兩輛車位於同一個停車位的情況下發生錯誤。使用時間表可以允許您使用簡單的整數ID重新使用多個停車位的相同時間數據。

所以,你的數據表可能看起來像這樣

臺車 car_id(整數/數字的最快指數) ...

表車位 space_id 位置

表時隙 time_id整數 begin_datetime(除非你必須使用秒數!) end_time(不要使用秒,除非你必須!)

現在,這裏是它獲得樂趣。使用由car.car_id + parking_space.space_id + time_id組成的組合主鍵添加中間表。還有其他的東西可以添加到這裏進行優化,但是我希望你明白了。

表預訂 car_id PK parking_space_id PK TIME_ID PK(它是一個整數 - 只是儘量保持它的高度細化越好 - 增量30分鐘或東西 - 如果你讓這包括秒/毫秒/等優勢被取消,因爲你不能重複使用時間表中的相同值) (這也是存儲可變利率,折扣等不同於這個特定賬戶,預訂等的地方)。

現在,您可以減少數據量,因爲您沒有複製連接表(預留)中的時間戳。通過使用整數,您可以將多個停車位重複使用該時間段,但您也可以應用一個限制,以防止兩輛汽車在給定日期/時間範圍內租用給定地點的相同「時間段」。這也可以更容易地存儲關於客戶的一些歷史記錄 - 誰知道,您可能希望看到關於更頻繁租賃的客戶的報告並提供折扣等。

通過使用三元關係模型,您可以使每個點在給定時間段內唯一(可能帶有一些添加的驗證規則),因此係統只能在一個給定時間段內將一輛車存儲在一個停車點中。

通過使用整數作爲鍵而不是時間戳,可以確保數據庫不需要做任何繁重的索引鍵和排序/查詢。當您擁有海量數據集並且需要高效率時,這是數據倉庫/ OLAP報告中的常見做法。我認爲它也適用於此。

相關問題