2011-04-26 151 views
6

我正在構建一個應用程序,用戶可以相互連接(就像任何社交網絡中的朋友一樣)。「朋友的朋友」喜歡sql查詢

我保存在一個表具有以下結構的連接:

id_user1 | id_user2 | is_accepted | is_blocked | created_at 

用戶之間的連接是雙向的,因此當兩個用戶連接只有一個表中的記錄。如果user_idid_user1id_user2列表中,則無關緊要。

現在我需要編寫一個sql查詢來獲取某些用戶的「朋友的朋友」,這些用戶並不是用戶的朋友。 此外,用戶必須被接受並且不被阻止。

恢復中,這裏是我需要執行的步驟。

  1. 找到與我希望用戶(id_user1 = current_userid_user2 = current_useris_accepted!blocked

  2. 的foreach返回user_ids的相關聯的所有用戶ID - >獲取所有相關用戶(忽略與當前協會用戶)(確保它也是accepted!blocked)。

我該怎麼做這樣的查詢?

感謝您的幫助。

+2

我不認爲你真的希望這張桌子是雙向的。如果A阻止B但B仍然想從A聽到?更不用說,如果你這樣做,那麼做自我加入會容易得多。 – drysdam 2011-04-26 16:14:50

+0

我想如果A阻止B,不僅他不想聽到B,而且也不想讓B聽到他的消息。 – Andre 2011-04-26 16:19:13

+0

如果你想要達到一個級別,那麼自我加入會做...如果你想多走一個級別,那麼我想層次查詢更好,但我不知道如何在MySQL中實現。 – 2011-04-26 16:22:13

回答

2
SELECT CASE f2.id_user1 WHEN CASE f1.id_user1 WHEN $user THEN f1.id_user2 ELSE f1.id_user1 END THEN f2.id_user2 ELSE f2.id_user1 END 
FROM friends f1 
JOIN friends f2 
ON  f2.id_user1 = CASE f1.id_user1 WHEN $user THEN f1.id_user2 ELSE f1.id_user1 END 
     OR f2.id_user2 = CASE f1.id_user1 WHEN $user THEN f1.id_user2 ELSE f1.id_user1 END 
WHERE (f1.id_user1 = $user OR f1.id_user = $user) 
     AND f1.is_accepted = 1 
     AND f2.is_accepted = 1 
     AND f1.is_blocked = 0 
     AND f2.is_blocked = 0 
     AND NOT (f1.id_user1, f1.id_user2) = (f2.id_user1, f2.id_user2) 

請注意,最好先存儲用戶,最好是第二位。在這種情況下,查詢會更簡單。

+0

我已經測試過它,它的工作原理。謝謝 – brpaz 2011-04-27 08:14:18

1

當直接使用每條記錄的one-record-table時,所有查詢都會變得臃腫且容易出錯,因爲您經常需要編寫'id_user1 = ...或id_user2 = ...'。我會創建一個視圖

CREATE VIEW bidifreinds (id_user1, id_user2, is_accepted, is_blocked, created_at) AS 
SELECT id_user1, id_user2, is_accepted, is_blocked, created_at FROM friends 
UNION 
SELECT id_user2, id_user1, is_accepted, is_blocked, created_at FROM friends 

這會讓生活變得更容易。

然後,你可以寫

SELECT f1.id_user1, f2.id_user2 
FROM friends f1, friends f2 
WHERE f2.id_user1 = f1.id_user2 
    AND f1.is_accepted 
    AND NOT f1.is_blocked 
    AND f2.is_accepted 
    AND NOT f2.is_blocked 

我希望你不usind的MySQL,因爲MySQL是在查詢視圖上的速度很慢。

0
select id_user1 from friends where is_accepted = 1 and is_blocked = 0 and id_user2 in  
(select id_user1 from friends where is_accepted = 1 and is_blocked = 0 and id_user2 = :a_user: 
    union 
    select id_user2 from friends where is_accepted = 1 and is_blocked = 0 and id_user1 = :a_user:) 
union 
select id_user2 from friends where is_accepted = 1 and is_blocked = 0 and id_user1 in 
(select id_user1 from friends where is_accepted = 1 and is_blocked = 0 and id_user2 = :a_user: 
    union 
    select id_user2 from friends where is_accepted = 1 and is_blocked = 0 and id_user1 = :a_user:) 

您可以添加whereClause淘汰:a_user:從結果。

4

由於其他人提到的原因,並且因爲我已經看到它在其他系統中運行得更好,所以我會爲每個方向連續行。

primary_user_id | related_user_id | is_accepted | is_blocked | created_at 

然後你也可以創建在USER_ID應足以抵消加倍行數的開銷聚集索引。然後

你的第一個查詢將轉化爲這樣的事情:

SELECT f1.related_user_id 
FROM friends f1 
WHERE f1.primary_user_id = @current_user 
AND f1.is_accepted = 1 AND f1.is_blocked = 0 
AND EXISTS (
    SELECT * 
    FROM friends f2 
    WHERE f1.related_user_id = f2.primary_user_id 
    AND f2.related_user_id = @current_user 
    AND f2.is_accepted = 1 AND f2.is_blocked = 0 

不知道你是否能在MySQL做表函數。如果是這樣,然後將其包裝到一個函數中,以使您的第二個查詢更簡單。

+0

我會研究這個解決方案。謝謝。 – brpaz 2011-04-27 08:23:48