2014-05-07 112 views
0

我在MySQL(5.5.31)中有一個約有20M行的表。下面的查詢:DISTINCT導致全表掃描

SELECT DISTINCT mytable.name name FROM mytable 
LEFT JOIN mytable_c ON mytable_c.id_c = mytable.id 
WHERE mytable.deleted = 0 ORDER BY mytable.date_modified DESC LIMIT 0,21 

引起全表掃描,以講解說typeALL和額外的信息是Using where; Using temporary; Using filesort。解釋的結果:

id select_type table   type possible_keys key  key_len ref   rows  Extra 
    1 SIMPLE  mytable   ALL  NULL   NULL NULL NULL  19001156 Using where; Using temporary; Using filesort 
    1 SIMPLE  mytable_c  eq_ref PRIMARY   PRIMARY 108  mytable.id 1   Using index 

沒有加入解釋的樣子:

id select_type table   type possible_keys key   key_len ref   rows  Extra 
    1 SIMPLE  mytable   index NULL   mytablemod 9  NULL  21   Using where; Using temporary 

id_cmytable_cmytable_c主鍵不具備的每一行不止一行在mytabledate_modified已編入索引。但看起來像MySQL不明白這一點。如果我刪除了DISTINCT子句,那麼explain使用索引,並且只觸及21行就像預期的那樣。如果我刪除連接它也這樣做。如果沒有連接的全表掃描,有沒有辦法讓它工作? explain顯示mysql知道它只需要mytable_c中的一行,它正在使用主鍵,但仍在mytable上進行全面掃描。

DISTINCT是由於ORM系統生成查詢的原因,在ORM系統中可能會有多行可能由JOIN生成的情況,但SELECT字段的值將始終是唯一的(即,如果JOIN是針對多個 - 值鏈接只有在每個連接行中相同的字段將在SELECT中)。

+0

對於沒有列選擇的表,您有一個外連接。我不明白。 – Strawberry

+0

@Strawberry這個查詢有點簡單,但它*仍*導致全表掃描。這是奇怪的部分 - 無論我是否包含其他表的字段,都會發生全表掃描。 – StasM

+0

試圖回答這種問題而沒有看到a)解釋和b)正確的DDLs是愚蠢的。 (並不是說即使有這些信息我也能做得更好!) – Strawberry

回答

1

這些只是通用的註釋,而不是mysql特定的。

要從mytable中找到所有可能的name值,需要對錶或索引進行全面掃描。可能的選項:

  • 全表掃描
  • 開始 deleted(利用過濾器)開始 name(僅用於輸出關注的列的索引的
  • 全索引掃描索引的
  • 全索引掃描)

如果有上deleted索引,服務器可以找到所有的deleted = 0索引條目,然後查找從表中對應的name值。但是,如果deleted的基數較低,或者統計數據不會有不同的說法,那麼執行首次索引的雙讀取然後再讀取相應的數據項可能會更加昂貴。在這種情況下,只需掃描表格即可。

如果在name上有索引,索引掃描可能就足夠了,但是需要檢查該表以檢查過濾器。再次頻繁跳轉從索引到表格。

連接列也需要以類似的方式考慮。

如果您忘記了連接部分並在列name,deleted上有多部分索引,則可能會發生索引掃描。

更新

對我來說,DISTINCTORDER BY部分是有點混亂。其中name記錄是date_modified用於排序?我覺得這樣的事情會多一點明確:

SELECT mytable.name name --, MIN(mytable.date_modified) 
    FROM mytable 
    LEFT JOIN mytable_c ON mytable_c.id_c = mytable.id 
    WHERE mytable.deleted = 0 
    GROUP BY mytable.name 
    ORDER BY MIN(mytable.date_modified) DESC LIMIT 0,21 

無論哪種方式,一旦ORDER BY進場時,一個完整的掃描需要做找訂單。沒有ORDER BY,找到的第21個就足夠了。

+0

刪除的確有傾向於非常低的選擇性,它只有兩個值(0和1),對於幾乎所有的行都是0.但它不需要找到'name'的所有值,只有21個不同的值?的確,這就是沒有加入的情況。但加入後,它會進行全面掃描。爲什麼? – StasM

+0

@StasM通過以下鏈接查看關於獨特和排序的內容:http://dev.mysql.com/doc/refman/5.7/en/distinct-optimization.html – Glenn

+0

但是,這並非如此 - 即使訂單已滿除非有聯接,否則不會發生掃描。它也不應該發生,因爲來自date_modified索引的前21條記錄完成查詢(所有名稱都是不同的),並且不需要額外的記錄。只是注意到我忘記提及 - 'date_modified'有一個索引。 – StasM

0

爲什麼不嘗試移動條件mytable.deleted = 0從哪裏到JOIN ON?您也可以嘗試使用FORCE INDEX(mytablemod)