2011-11-12 24 views
1

我想要一個包含列的表:id,user_id1和user_id2。SQL構造一個雙向圖

基本上,這將使用鏈接表示user1與user2是朋友,反之亦然。

我的設置獲取單個用戶,然後列出他們的朋友。考慮到我不想在我的表中添加額外的條目,我該如何處理這個問題?

我想做的事情是這樣的:insert into friendship (user_id1, user_id2) values (<id1>, <id2>) where ...

,但我不知道怎麼做這樣的條件邏輯在SQL

+0

你問的是如何防止重複或者你問如何檢索特定用戶的所有朋友? –

+0

兩者。只是想弄清楚最好的架構。 – lollercoaster

回答

3

你可能會迫使user_id1總是小於user_id2與CHECK約束:

CHECK (user_id1 < user_id2) 

大概人們不允許做自己的朋友。然後在INSERT之前確保ID的順序是正確的。在抽取的朋友列表,你仍然必須檢查雖然兩列:

select user_id2 from friendship where user_id1 = X 
union all 
select user_id1 from friendship where user_id2 = X 

其中X,當然,你感興趣的人,看看如果兩個人是朋友,只是。按照正確的順序排列他們的ID並選擇退出。

+0

如何將較小的值id作爲user_id1提供幫助? – lollercoaster

+0

@lollercoaster:避免重複。無向意味着'(1,2)'和'(2,1)'是相同的邊緣,所以如果你強制(​​使用CHECK)第一個節點的ID低於第二個節點,你就不會同時獲得'(1,2)'和'(2,1)'因爲'(2,1)'會違反檢查約束。 –

+0

是的不幸CHECKs和CONSTRAINTs不適用於MySQL。他們被解析,但在插入/更新調用時被忽略 – lollercoaster

2

如果它的索引正確,我發現存儲user1, user2以及user2, user1都沒有問題。

如果您確定只需要一行,那麼我建議您始終在user1中存儲最低的ID(可能甚至在該列上施加一個約束來強制執行),並且您將會看到一個朋友查詢像這樣:

SELECT user1 FROM friendship WHERE user2 = $user_id 
UNION ALL 
SELECT user2 FROM friendship WHERE user1 = $user_id 
+0

我只是擔心存儲?似乎愚蠢地存儲每個兩次.. – lollercoaster

+0

它使您的查詢更簡單。另外,如果您始終將最低值存儲在user1中,則不能有重複項。如果用戶14的朋友用戶18,那麼你存儲14,18。但是,如果用戶18與用戶14交好了,那麼你也會存儲14,18。 –

+2

UNION ALL可能比UNION更好的調用,對數據庫的工作更少,重複也不應該發生。 –

0
INSERT INTO friendship (user_id1, user_id2) 
SELECT <id1>,<id2> FROM <special table with only one row, contents irrelvant> 
WHERE ((<id1>,<id2>) NOT IN 
     ((SELECT user_id1, user_id2 FROM friendship) 
     UNION 
     (SELECT user_id2, user_id1 FROM friendship))) 

從底部解釋到頂部,我們首先構建所有對的表格中任一方向。然後我們發現是否有(<id1>, <id2>)。如果是,則第2行的SELECT查詢不會返回元組,因此不會插入任何元組。如果不是,則通過SELECT查詢返回一個元組(特別是元組(<id1>,<id2>))並插入它。

我認爲a_horse_with_no_name的答案可能是在這個特定情況下和大多數類似情況下的最好答案,但是如果有人在某種情況下添加這樣的索引,或者不允許或者會導致問題(例如,if有一個更復雜的表,其中只有一些行有有意義的友誼條目,但你仍然想要唯一性),那麼這個查詢就可以完成這項工作。

+0

我不明白什麼特別的表與一行是...? – lollercoaster

+0

讓我們假設你正在做一個SELECT查詢,你只需要1的值。如果你做SELECT 1 FROM友誼,你會得到友誼表中每一行的1。所以,當你使用SELECT來計算值時,你需要一個只有一行的表,這樣你最多隻能得到一個值。一些RBDMS爲此有一個特殊的表格,但我不知道你使用的是什麼RBDMS,所以我不知道該怎麼稱呼它。但是你可以用一行創建任何表並使用它。 –

4

爲了防止插入「邏輯」相同的組合,你可以創建唯一索引:

CREATE UNIQUE INDEX unique_pair 
     ON your_table (least(user_id1, user_id2), greatest(user_id1, user_id2)); 

這是標準的ANSI SQL,並應在大多數DBMS工作。如果您的DBMS不支持leastgreatest,則可以使用CASE語句來實現相同的功能,只是不夠緊湊。

它是如何工作的?

通過始終把較低的估值師第一和更高價值的第二個索引,元組(2,1)(1,2)都將被收錄爲(1,2) - 在UNIQUE沒有休息。

+0

有趣!那麼如果我嘗試插入一對(13,10),會發生什麼?會有什麼方法來捕捉這樣的錯誤?另外,如果我理解正確,這個唯一索引是作爲插入到表中的約束? – lollercoaster

+0

這是CHECK約束的一個有趣的替代方案,甚至應該與MySQL一起工作。 –

+0

在這種情況下,添加(13,10)不是錯誤。它只是添加它併爲它創建一個索引(10,13)。然後,如果您稍後嘗試添加(10,13),該索引也將具有(10,13)的索引,因此它將被拒絕。 –