2015-12-07 80 views
0

我有一些表:MySQL的排序上加入表列極其緩慢(臨時表)

object 
person 
project 
[...] (some more tables) 
type 

對象表有外鍵的所有其他表。

現在我喜歡的查詢:

SELECT * FROM object 
LEFT JOIN person ON (object.person_id = person.id) 
LEFT JOIN project ON (object.project_id = project.id) 
LEFT JOIN [...] (all other joins) 
LEFT JOIN type ON (object.type_id = type.id) 
WHERE object.customer_id = XXX 
ORDER BY object.type_id ASC 
LIMIT 25 

這工作得很好,速度快,即使是大的結果集。例如,我有90000個對象,查詢大約需要3秒。結果非常大,因爲這些表有很多列,並且所有列都被提取。有關信息:我將Sympel與Propel,InnoDB和「doSelectJoinAll」功能結合使用。

但如果做一個查詢像(由type.name排序):

SELECT * FROM object 
LEFT JOIN person ON (object.person_id = person.id) 
LEFT JOIN project ON (object.project_id = project.id) 
LEFT JOIN [...] (all other joins) 
LEFT JOIN type ON (object.type_id = type.id) 
WHERE object.customer_id = XXX 
ORDER BY type.name ASC 
LIMIT 25 

的查詢需要約200秒!

說明:

id | select_type | table  | type  | possible_keys | key  | key_len | ref   | rows  | Extra 
1 | SIMPLE | object | ref  | object_FI_2 | object_FI_2 | 4  | const   | 164966 | Using where; Using temporary; Using filesort 
1 | SIMPLE | person | eq_ref | PRIMARY | PRIMARY | 4   | db.object.person_id | 1  
1 | SIMPLE | ...  | eq_ref | PRIMARY | PRIMARY | 4   | db.object...._id | 1  
1 | SIMPLE | type  | eq_ref | PRIMARY | PRIMARY | 4   | db.object.type_id  | 1  

我在PROCESSLIST看到,MySQL正在對連接表這樣的排序創建臨時表。

將索引添加到type.name中並沒有提高性能。只有大約800個類型的行。

我發現很多的連接和大的結果是問題,因爲如果我做一個查詢只有一個連接,如:

SELECT * FROM object 
LEFT JOIN type ON (object.type_id = type.id) 
WHERE object.customer_id = XXX 
ORDER BY type.name ASC 
LIMIT 25 

它的工作原理預期的快。

有沒有一種方法來改善這樣的排序查詢上有很多連接表的大結果集?或者,在連接的表格列上排序是一種壞習慣,而這不應該完成?

謝謝

+1

用EXPLAIN結果編輯你的問題 – Mihai

+0

好吧,我爲有問題的查詢添加了解釋結果 – CrashOverwrite

回答

0

LEFT獲取重新安排表的順序的方式。它有多快沒有任何LEFT?你有同樣的答案嗎?

LEFT可能是一個紅色的鯡魚......這裏的優化是什麼容易做:

  1. 決定什麼爲了把表考慮任何WHERE過濾和任何LEFTs。由於WHERE object.customer_id = XXX,object很可能是最好的表。
  2. object獲取滿足WHERE的行。
  3. 從其他表中獲取所需的列(執行JOINs)。
  4. 根據ORDER BY排序**見下方
  5. 交付前25行。

**讓我們更深入地瞭解這兩個:

WHERE object.customer_id = XXX ORDER BY object.id 
WHERE object.customer_id = XXX ORDER BY virtually-anything-else 

你有INDEX(customer_id),是否正確?這個表是InnoDB,對嗎?那麼,每個二級索引都隱含地包含PRIMARY KEY,就好像你曾說過INDEX(customer_id, id)一樣。第一個WHERE + ORDER BY的最佳索引就是這樣。它將找到XXX並掃描25行,然後停止。你可能會說,步驟2,4,5混合在一起。

第二個WHERE只是收集所有的東西,通過第4步。這可能是成千上萬的行。因此它可能會慢很多。請參閱article on building optimal indexes

+0

謝謝你的anwser。我們使用的是Symfony 1.4,不幸的是,JOIN LEFTs是由框架自動完成的,所以我無法擺脫它。 – CrashOverwrite

+0

是的,我們正在使用InnoDB,我會將這些信息添加到我的問題中。 是的,我有索引(customer_id),因爲所有外鍵都有索引。 我不明白爲什麼對象表中的INDEX(customer_id,id)會幫助,如果我按type.name排序? – CrashOverwrite

+0

您可以使用mysql命令行工具對'SELECTs'進行試驗,看看它有多快和沒有'LEFT'。 –