2013-03-04 39 views
1

我有對稱用戶關係的表,從表中選擇:有效的方法,其中條件1或反轉條件1

CREATE TABLE IF NOT EXISTS `friends` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `user_a` int(11) NOT NULL DEFAULT '0', 
    `user_b` int(11) NOT NULL DEFAULT '0', 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 

此表包含以下信息:

  • 用戶ID爲1 ID爲2的用戶的朋友
  • ID爲3的用戶的朋友是ID爲1的用戶的朋友

的結論是:

  • 用戶ID爲1具有2個朋友(ID 3和ID 2)
  • 用戶的ID可以是在任一兩列的(見在用戶ID 1)

如何進行高效查詢以檢查用戶1是否是用戶3的好友?

爲什麼我問一個有效的方法?那麼,因爲我有3個不同的解決方案(可能還有更多),但我正努力選擇其中最有效的方法。任何幫助?

方法1:

SELECT user_b AS user_a 
FROM friends 
WHERE (user_a = :user_a AND user_b = :user_b) 
UNION ALL 
SELECT user_a 
FROM friends 
WHERE (user_b = :user_b AND user_a = :user_a) 

方法2:

SELECT * FROM friends WHERE (user_a = :user_a AND user_b = :user_b) OR 
(user_b = :user_a AND user_a = :user_b) 

方法3:

SELECT user_a FROM (
SELECT user_b AS user_a 
FROM friends 
WHERE user_a = :user_a 
UNION ALL 
SELECT user_a 
FROM friends 
WHERE user_b = :user_a 
) AS newtab WHERE newtab.user_a = :user_b; 

PHP檢查:

$my_id = 1; 
$friend_id = 3; 
$stmt = $dbh->prepare("SELECT ..."); // approach 1 or 2 or 3 or ... 
$stmt->bindParam(':user_a', $my_id, PDO::PARAM_STR); 
$stmt->bindParam(':user_b', $friend_id, PDO::PARAM_STR); 
$stmt->execute(); 

if ($stmt->rowCount() > 0) { 
echo "You are friends";} 
else { echo "he is not your friend";} 

性能明智 - 哪種方法更好?

編輯:

測試

$start_2 = microtime(true); 
for ($i = 1; $i <= 100; $i++) { 
    $stmt->execute(); 
} 
$end_2 = microtime(true); 

結果

1:0.14095306396484

2:0.063449859619141

3:0.18946194648743

+1

性能問題通常最終成爲「基準測試並親自體驗」。因爲你的user_a/user_b列都沒有索引,所以不管解決方案如何,你最終都會進行全表掃描。 – 2013-03-04 18:34:49

+0

@MarcB我遵照你的建議,實施了benchark。獲獎者是方法2) – Alex 2013-03-04 18:51:55

+0

怎麼樣'SELECT * 從那裏(USER_A =朋友 :USER_A OR USER_A =:USER_B) AND(USER_B =:USER_A OR USER_B =:USER_B) LIMIT 1;' – SparKot 2013-03-04 19:05:39

回答

1

正如你已經通過測試發現的方法2將會更快。

我的理由是,在50%的情況下,第一部分將足以滿足WHERE部分,第二部分根本不會執行。

從@Luis中添加你總是擁有user_a < user_b這些50%的想法將上升到100%。

另外,連接和子查詢可能需要臨時表,有時甚至必須在磁盤上。這很慢,所以應該避免。

要測試查詢使用臨時表運行EXPLAIN並在額外部分使用臨時尋找

我也擺脫了額外的ID(無用的數據),並把user_a,user_b作爲主鍵。這會給你一個快速的索引(只要你知道user_a < user_b)。

+0

感謝您的解釋,是有道理的 – Alex 2013-03-04 19:10:06

1

你必須創建爲usersID索引,並檢查是否你真的需要額外的ID。

爲簡單起見,我會使用選項2,不確定它是否是最有效的方法,您可以測試一個更快更簡單。

我不得不做一些類似於此的選擇查詢,其中比插入要多得多,並且有很多記錄,所以我所做的總是以特定順序插入,在這種情況下可能是某種東西像user_a < user_b,所以你只能在查詢中檢查一邊。

+0

基本上我得到了2個表格:第一個是用於成員(id,name,surname),第二個用於關係(id1,id2)。這樣,我可以建立2個用戶之間的友誼。有沒有更好的方法來做到這一點?你談論的「一邊查詢」方法是好的 - 但這意味着表格會有兩倍多的記錄 – Alex 2013-03-04 18:54:30

+0

並非並不意味着,它只意味着如果1是3的朋友,它只能在這個中間表,1個id將始終在user_a中,3個將在user_b中,因爲1 <3 – 2013-03-04 18:58:18

+1

哦,對!我在想別的事情,但現在我明白了你的觀點!基本上我必須比較ID並按照「X Alex 2013-03-04 19:03:13