2008-10-09 75 views
15

我一直在分析我正在處理的應用程序中的一些查詢,並且遇到了一個查詢,該查詢檢索的行數多於必要的數量,結果集在應用程序代碼中被裁減。左連接優於內連接?

將LEFT JOIN更改爲INNER JOIN將結果集修剪爲所需內容,並且可能還會更高效(因爲選擇的行數更少)。事實上,LEFT JOINED的查詢表現超過INNER JOIN'ED,花了一半時間完成。

LEFT JOIN:(127點總的行,查詢花費0.0011秒)

INNER JOIN:(10點總的行,查詢花費0.0024秒)

(我跑的查詢多次,並且這些是平均數) 。

運行EXPLAIN上都顯示沒有任何解釋的性能差異:

對於INNER JOIN:

id select_type  table type possible_keys key  key_len  ref  rows  Extra 
1 SIMPLE contacts  index  NULL  name  302  NULL   235 Using where 
1 SIMPLE lists   eq_ref  PRIMARY  PRIMARY  4 contacts.list_id  1 
1 SIMPLE lists_to_users eq_ref  PRIMARY  PRIMARY  8 lists.id,const 1  
1 SIMPLE tags   eq_ref  PRIMARY  PRIMARY  4 lists_to_users.tag_id 1  
1 SIMPLE users   eq_ref  email_2  email_2  302  contacts.email 1 Using where 

對於LEFT JOIN:

id select_type  table type possible_keys key  key_len  ref  rows Extra 
1 SIMPLE   contacts index  NULL  name  302  NULL 235  Using where 
1 SIMPLE  lists  eq_ref  PRIMARY  PRIMARY  4 contacts.list_id 1  
1 SIMPLE lists_to_users eq_ref  PRIMARY  PRIMARY  8 lists.id,const 1  
1 SIMPLE   tags  eq_ref  PRIMARY  PRIMARY  4 lists_to_users.tag_id 1  
1 SIMPLE  users  eq_ref  email_2  email_2  302  contacts.email 1 

和查詢本身:

SELECT `contacts`.*, `lists`.`name` AS `group`, `lists`.`id` AS `group_id`, `lists`.`shared_yn`, `tags`.`name` AS `context`, `tags`.`id` AS `context_id`, `tags`.`color` AS `context_color`, `users`.`id` AS `user_id`, `users`.`avatar` 
FROM `contacts` 
LEFT JOIN `lists` ON lists.id=contacts.list_id 
LEFT JOIN `lists_to_users` ON lists_to_users.list_id=lists.id AND lists_to_users.user_id='1' AND lists_to_users.creator='1' 
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 
INNER JOIN `users` ON users.email=contacts.email 
WHERE (contacts.user_id='1') 
ORDER BY `contacts`.`name` ASC 

(我所說的子句是「用戶」表上的最後一個INNER JOIN)

查詢在MySQL 5.1數據庫上運行,如果它有所不同。

有沒有人有一個線索,爲什麼在這種情況下,LEFT JOIN的查詢優於INNER JOIN?

更新:由於Tomalak的建議,我使用的小表使INNER JOIN更復雜,我創建了一個包含一些模擬數據的測試數據庫。 「用戶」表格爲5000行,聯繫人表格爲〜500,000行。結果是一樣的(時間也沒有改變,當你認爲表格現在更大時,這是令人驚訝的)。

我也在聯繫人表上運行ANALYZE和OPTIMIZE。沒有做出任何明顯的區別。

+0

您是否嘗試先放置內連接? – 2008-10-09 06:10:02

+0

我有,它確實加快了20%的查詢速度,但仍然比左加入速度更慢 – 2008-10-09 06:14:43

+0

嘗試按順序構建每個查詢(加入一個表,測量,加入下一個等等)也許這可以幫助您確定慢速操作。 – Tomalak 2008-10-09 06:21:48

回答

6

這可能是由於INNER JOIN必須檢查兩個表中的每一行來查看列值(電子郵件)是否匹配。無論如何,LEFT JOIN都會從一個表中返回。如果它被索引,那麼它也會知道該怎麼做。

4

表基數對查詢優化器有影響。我猜想,小型表格會讓內連接成爲更復雜的操作。只要有更多的記錄比DB服務器願意保留在內存中,內部聯接可能會開始超過左側聯接。

2

即時通訊您正在陷入稱爲過早優化的陷阱。查詢優化器是瘋狂變幻莫測的事情。我的建議是繼續前進,直到你確定某個連接是否有問題。

-3

LEFT JOIN返回的行比INNER JOIN多,因爲這兩個不同。
如果LEFT JOIN在查找的表中找不到相關條目,它將返回表的NULL。
但是,如果INNER JOIN沒有找到相關條目,它根本不會返回整個行。

但你的問題,你有query_cache啓用? 嘗試用

SELECT SQL_NO_CACHE `contacts`.*, ... 

除此之外,運行查詢,我會填充表與數據越多,跑

ANALYZE TABLE t1, t2; 
OPTIMIZE TABLE t1, t2; 

看看會發生什麼。

12

如果你認爲LEFT JOIN的實現是INNER JOIN +更多的工作,那麼這個結果是令人困惑的。如果INNER JOIN的實現是(LEFT JOIN +過濾)會怎麼樣?啊,現在很清楚。

在查詢計劃中,唯一不同的是:users ... extra:using where。這意味着過濾。在帶有內部聯接的查詢中有一個額外的篩選步驟


這是一種不同於通常在where子句中使用的過濾。在A上創建索引以支持此過濾操作很簡單。

SELECT * 
FROM A 
WHERE A.ID = 3 

考慮這個查詢:

SELECT * 
FROM A 
    LEFT JOIN B 
    ON A.ID = B.ID 
WHERE B.ID is not null 

該查詢等效於內連接。 B上沒有索引會幫助進行過濾操作。其原因是,where子句是在B.

述明的加入結果的條件,而不是條件
0

試試這個:

SELECT `contacts`.*, `lists`.`name` AS `group`, `lists`.`id` AS `group_id`, `lists`.`shared_yn`, `tags`.`name` AS `context`, `tags`.`id` AS `context_id`, `tags`.`color` AS `context_color`, `users`.`id` AS `user_id`, `users`.`avatar` 
FROM `contacts` 
INNER JOIN `users` ON contacts.user_id='1' AND users.email=contacts.email 
LEFT JOIN `lists` ON lists.id=contacts.list_id 
LEFT JOIN `lists_to_users` ON lists_to_users.user_id='1' AND lists_to_users.creator='1' AND lists_to_users.list_id=lists.id 
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 
ORDER BY `contacts`.`name` ASC 

這應該給你額外的性能,因爲:

  • 您在任何「左」或「右」連接出現之前放置所有內連接。在應用後續外連接之前,這會過濾掉一些記錄。
  • 「AND」運算符的短路(「AND」的順序很重要)。如果列和文字之間的特點比較是錯誤的,它不會執行所需的表掃描的表的PK和FKS

之間的特點比較。如果你沒有找到任何的性能提升,然後全部更換「COUNT(*)」的列集並進行左/內部測試。這樣一來,不管查詢,你會只檢索1單行1個單柱(伯爵),這樣你就可以丟棄返回的字節數是您的查詢的緩慢的原因:

SELECT COUNT(*) 
FROM `contacts` 
INNER JOIN `users` ON contacts.user_id='1' AND users.email=contacts.email 
LEFT JOIN `lists` ON lists.id=contacts.list_id 
LEFT JOIN `lists_to_users` ON lists_to_users.user_id='1' AND lists_to_users.creator='1' AND lists_to_users.list_id=lists.id 
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 

祝你好運