2012-12-31 90 views
7
explain 
select 
    * 
from 
    zipcode_distances z 
inner join 
    venues v  
    on z.zipcode_to=v.zipcode 
inner join 
    events e 
    on v.id=e.venue_id 
where 
    z.zipcode_from='92108' and 
    z.distance <= 5 

我試圖找到所有「在5英里的郵政編碼92108以內的場地的活動」,但是,我很難優化此查詢。如何避免在此mysql查詢上進行全表掃描?

這是什麼解釋的樣子:

id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra 

1, SIMPLE, e, ALL, idx_venue_id, , , , 60024, 
1, SIMPLE, v, eq_ref, PRIMARY,idx_zipcode, PRIMARY, 4, comedyworld.e.venue_id, 1, 
1, SIMPLE, z, ref, idx_zip_from_distance,idx_zip_to_distance,idx_zip_from_to, idx_zip_from_to, 30, const,comedyworld.v.zipcode, 1, Using where; Using index 

我得到的「E」表進行全表掃描,我想不出我需要創建得到它什麼指數要快。

任何意見,將不勝感激

謝謝

+0

您是否需要結果集中所有表的所有列? –

+0

我試圖避免使用「in」子查詢。 – john

+0

我想要做的事情的俗語說明是找到郵政編碼駐留在郵政編碼中的場所,我發現郵政編碼靠近92108.因此,它加入場地,然後加入與該場地相關的活動。 – john

回答

7

基於對你的問題EXPLAIN輸出,你已經擁有的所有索引查詢應該使用,即:

CREATE INDEX idx_zip_from_distance 
    ON zipcode_distances (zipcode_from, distance, zipcode_to); 
CREATE INDEX idx_zipcode ON venues (zipcode, id); 
CREATE INDEX idx_venue_id ON events (venue_id); 

(我不是從你的目錄名稱確定idx_zip_from_distance是否真的包括zipcode_to列,如果不是,你應該添加它使其成爲covering index。另外,爲了完整性,我還包含venues.ididx_zipcode,但假設它是表的主鍵並且使用InnoDB,將自動包含在內。)

但是,它看起來像MySQL正在選擇一個不同的,可能不是最理想的查詢計劃,它掃描所有事件,查找他們的場所和郵政編碼,然後才按距離過濾結果。如果事件表的基數足夠低,這個可能是是最佳的查詢計劃,但是從你提出這個問題的事實,我認爲它不是。爲次佳的查詢計劃

一個原因可能是你有太多指標被混淆的策劃者的事實。例如,你是否真的需要郵政編碼表上所有這三個索引,因爲它存儲的數據可能是對稱的?就個人而言,我只會建議我上面描述的索引,並在(zipcode_to, zipcode_from)上加上一個唯一的索引(如果您沒有人工索引,也可以是主鍵)(最好按照該順序進行,以便偶爾查詢zipcode_to=?可以利用它)。然而,基於我做過的一些測試,我懷疑爲什麼MySQL選擇錯誤的查詢計劃的主要問題來自於表的相對基數。據推測,您的實際zipcode_distances表是巨大的,而且MySQL不夠聰明,無法完全瞭解WHERE條款中的條件是否真的縮小了它的範圍。

如果是這樣,最好的和簡單的解決辦法可能是簡單地force MySQL to use the indexes you want

select 
    * 
from 
    zipcode_distances z 
    FORCE INDEX (idx_zip_from_distance) 
inner join 
    venues v  
    FORCE INDEX (idx_zipcode) 
    on z.zipcode_to=v.zipcode 
inner join 
    events e 
    FORCE INDEX (idx_venue_id) 
    on v.id=e.venue_id 
where 
    z.zipcode_from='92108' and 
    z.distance <= 5 

隨着該查詢,你確實應該得到理想的查詢計劃。 (你需要FORCE INDEX這裏,因爲只有USE INDEX查詢規劃仍然可以決定使用一個表掃描,而不是建議的指數,擊敗目的。我有這種情況發生時,我第一次測試這一點。)

詩篇。這是一個關於SQLize的演示,演示了這個問題withwithoutFORCE INDEX

0

你可以使用子查詢:

select * from zipcode_distances z, venues v, events e 
where 
    z.id in (select id from zipcode z where z.zipcode_from='92108' and z.distance <= 5) 
    and z.zipcode_to=v.zipcode 
    and v.id=e.venue_id 
1

有兩個表中的索引中的列?

e.id and v.venue_id 

如果您不這樣做,請在兩個表中創建索引。如果您已經有了,那麼可能是因爲您在一個或多個表中的記錄數很少,並且分析器檢測到執行全面掃描而不是索引讀取效率更高。

0

您正在選擇所有表(select *)中的所有列,因此,當查詢引擎必須從索引到每個單一行上的表執行查找時,優化器中沒有什麼地方會使用索引。