2010-02-15 24 views
4

我試圖優化這個慢查詢(> 2秒)優化SQL查詢

SELECT COUNT(*) 
FROM crmentity c, mdcalls_trans_activity_update mtu, mdcalls_trans mt 
WHERE (mtu.dept = 'GUN' OR mtu.dept = 'gun') AND 
     mtu.trans_code = mt.trans_code AND 
     mt.activityid = c.crmid AND 
     MONTH(mtu.ts) = 2 AND 
     YEAR(mtu.ts) = YEAR(NOW()) AND 
     c.deleted = 0 AND 
     c.smownerid = 28 

這是輸出,當我使用EXPLAIN:

id select_type table type possible_keys key key_len ref rows Extra 
1 SIMPLE c index_merge PRIMARY,crmentity_smownerid_idx,crmentity_deleted_smownerid_idx,crmentity_smownerid_deleted_idx crmentity_smownerid_idx,crmentity_deleted_smownerid_idx 4,8 NULL 91 Using intersect(crmentity_smownerid_idx,crmentity_deleted_smownerid_idx); Using where; Using index 
1 SIMPLE mt ref activityid activityid 4 pharex.c.crmid 60 
1 SIMPLE mtu ref dept_idx dept_idx 5 const 1530 Using where 

它使用我創建的索引(dept_idx ),但仍需要2秒鐘才能對1,380,384條記錄的數據集運行查詢。是否有另一種以最佳方式表達此查詢的方式?

更新:使用David的建議,查詢現在下降到幾毫秒而不是運行超過2秒(實際上,在MySQL 5.0版本上爲51秒)。

+0

我會寫'WHERE lower(mtu.dept)='gun'AND ...'但我認爲你的數據庫已經以這種方式優化了它。 – initall

+0

我發現,至少在Oracle中,在查詢的lhs中使用較低的值會導致大規模的減速。與其他字符串比較相比是否會導致更慢的速度... –

+1

在列上使用lower()是不使用任何索引的好方法。這將解釋你的減速。 –

回答

6

什麼是最有選擇性的部分條款WHERE?也就是說,哪種條件會從結果集中移除最有潛力的項目?

我猜這是mtu.ts過濾器。如果這是真的,你也應該索引mtu.ts列,並嘗試用這種方式限制索引的使用;例如通過使用BETWEEN運算符。

其他提示:

  • 附加加盟條款直接與JOIN ... ON()的加盟,這使得查詢更容易閱讀,既爲人類和優化
  • 避免計算常量查詢,像YEAR(NOW())
  • 避免在WHERE子句中選擇列的功能,如MONTH(mtu.ts)。這減少了大量使用索引的可能性。
  • 正常化您的數據以避免套管問題,如mtu.dept = 'GUN' OR mtu.dept = 'gun';桌上的一個UPDATE mtu SET dept = lower(dept)和一個合適的CHECK dept = lower(dept)將有助於避免這種瘋狂。
2
  1. 我會重寫查詢使用連接。這更清楚並給予優化者更好的機會。
  2. MONTH(mtu.ts)= 2,YEAR(mtu.ts)= YEAR(NOW()) - 之間更好地利用mtu.ts ..
+0

你會如何改寫?再次感謝。 – Francis

+1

從crmentityÇSELECT COUNT(*) 內上mtu.trans_code = mt.trans_code 其中 '20100201' 之間mtu.ts和 '20100228' 和上mt.activityid = c.crmid 內部聯接mdcalls_trans_activity_update MTU加入mdcalls_trans公噸(mtu.dept in('GUN','gun')and c.deleted = 0 and c.smownerid = 28 – burnall

+0

感謝這個例子。我在PHP中創建了一個函數來獲取月份的開始日期和結束日期並在'BETWEEN'聲明中使用它。 – Francis

0

您可以將文本字符串更改爲數字嗎?

0

我能看到的最明顯的解決方案是將COUNT(*)更改爲僅覆蓋單個字段名稱,否則您的索引可能接近無用!

0

作爲一般原則,分析這類問題的一種好方法是理解您的匹配數據,以欣賞其基數。

也就是說,命令您的查詢,以便最有選擇性的事情首先發生。 更有可能在您的數據中,該部門='GUN'或userId將是28.

Lasty,你有沒有考慮加入MT和MTU而不是過濾? 它可能會使您的查詢更快,因爲您將限制需要日期比較的數據量。

+0

發佈得太快了,基本上David Schmitt和Burnall在說什麼! –