2012-10-10 223 views
4

我有一個查詢,正在33秒的有沒有更好的方式來改寫它,我怎麼可以把它轉換到程序需要優化MySQL查詢

select ut.templateId, 
(
case 
when ut.reportTypeId=4 then 'Account' 
when ut.reportTypeId=5 then 'Campaign' 
when ut.reportTypeId=6 then 'AdGroup' 
when ut.reportTypeId=7 then 'Ad' 
when ut.reportTypeId=8 then 'Keyword' 
end 
)as ReportType , 
ur.reportId, 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) < 5 then a.reportId else 0 end) as '<5secs', 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) between 5 and 10 then a.reportId else 0 end) as '5-10secs', 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) between 11 and 20 then a.reportId else 0 end) as '11-20secs', 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) between 21 and 30 then a.reportId else 0 end) as '21-30secs', 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) between 31 and 60 then a.reportId else 0 end) as '31-60secs', 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) between 61 and 120 then a.reportId else 0 end) as '61-120secs', 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) between 121 and 1800 then a.reportId else 0 end) as '2-30mins', 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) > 1800 then a.reportId else 0 end) as '>30mins' 
from 

(select reportId,createdTS from T_ReportMonitor where status='EndSP')a, 
(select reportId,createdTS from T_ReportMonitor where status='BeginSP')b, 
(select templateId,reportTypeId,reportConsoleType from T_UserTemplate) ut, 
(select reportId,templateId,createdTS,modifiedTS,isDeleted from T_UserReport) ur 

where a.reportId=b.reportId 
and date(ur.createdTS) = 20120731 
and ut.templateId=ur.templateId 
and reportConsoleType in ('Adser','APIAdser') 
and ur.isDeleted=false 
and a.reportId=ur.reportId 
and ur.reportId!=313509 AND ur.reportId!=313510 AND ur.reportId!=313511 AND ur.reportId!=313512 AND ur.reportId!=313509 AND ur.reportId!=313510 AND ur.reportId!=313511 AND ur.reportId!=313512 AND ur.reportId!=313520; 

查詢的解釋結果是

+----+-------------+-----------------+------+---------------+------+---------+------+--------+--------------------------------+ 
| id | select_type | table   | type | possible_keys | key | key_len | ref | rows | Extra       | 
+----+-------------+-----------------+------+---------------+------+---------+------+--------+--------------------------------+ 
| 1 | PRIMARY  | <derived2>  | ALL | NULL   | NULL | NULL | NULL | 20071 |        | 
| 1 | PRIMARY  | <derived3>  | ALL | NULL   | NULL | NULL | NULL | 20072 | Using where; Using join buffer | 
| 1 | PRIMARY  | <derived5>  | ALL | NULL   | NULL | NULL | NULL | 148591 | Using where; Using join buffer | 
| 1 | PRIMARY  | <derived4>  | ALL | NULL   | NULL | NULL | NULL | 154030 | Using where; Using join buffer | 
| 5 | DERIVED  | T_UserReport | ALL | NULL   | NULL | NULL | NULL | 124008 |        | 
| 4 | DERIVED  | T_UserTemplate | ALL | NULL   | NULL | NULL | NULL | 151745 |        | 
| 3 | DERIVED  | T_ReportMonitor | ALL | NULL   | NULL | NULL | NULL | 60849 | Using where     | 
| 2 | DERIVED  | T_ReportMonitor | ALL | NULL   | NULL | NULL | NULL | 60849 | Using where     | 
+----+-------------+-----------------+------+---------------+------+---------+------+--------+--------------------------------+ 

我對那些在使用的列密鑰的where子句和任何其他的比較,但他們都不是在查詢中使用,這是由於它們導出查詢的原因。

+0

請與發佈數據的結構,使我們能夠做出優化的查詢使用http://sqlfiddle.com –

+0

並解釋你想得到的結果 –

+0

不要使用子查詢,而要使用連接。 – Nin

回答

2

的主要問題查詢它使用子查詢嗎? MySQL不能在子查詢上使用索引,因爲它基本上爲子查詢在內存(或磁盤上)創建了一個新表。嘗試進行連接。

試試這個

select ut.templateId, 
(
case 
when ut.reportTypeId=4 then 'Account' 
when ut.reportTypeId=5 then 'Campaign' 
when ut.reportTypeId=6 then 'AdGroup' 
when ut.reportTypeId=7 then 'Ad' 
when ut.reportTypeId=8 then 'Keyword' 
end 
)as ReportType , 
ur.reportId, 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) < 5 then a.reportId else 0 end) as '<5secs', 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) between 5 and 10 then a.reportId else 0 end) as '5-10secs', 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) between 11 and 20 then a.reportId else 0 end) as '11-20secs', 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) between 21 and 30 then a.reportId else 0 end) as '21-30secs', 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) between 31 and 60 then a.reportId else 0 end) as '31-60secs', 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) between 61 and 120 then a.reportId else 0 end) as '61-120secs', 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) between 121 and 1800 then a.reportId else 0 end) as '2-30mins', 
(case when timestampdiff(SECOND,b.createdTS,a.createdTS) > 1800 then a.reportId else 0 end) as '>30mins' 
from 
T_ReportMonitor as a JOIN T_ReportMonitor as b ON (a.reportId=b.reportId) JOIN T_UserReport as ur ON (a.reportId=ur.reportId) JOIN T_UserTemplate as ut ON (ut.templateId=ur.templateId) 
WHERE a.status='EndSP' AND b.status='BeginSP' 
and date(ur.createdTS) = 20120731 
and reportConsoleType in ('Adser','APIAdser') 
and ur.isDeleted=false 
and ur.reportId NOT IN (313509,313510,313511,313512,313509,313510,313511,313512,313520); 

確保有上T_ReportMonitor.reportId,T_ReportMonitor.status和T_UserReport.reportId的關鍵。

還有一件事會降低您的查詢。您使用的功能在:

date(ur.createdTS) 

這意味着MySQL將不得不處理每一行以查看該函數的結果。這甚至可能被證明是性能增幅最大的一次。嘗試要麼使該字段日期字段(或創建一個新的日期字段),或者使用類似

WHERE ur.createdTS>='2012-07-31 00:00:00' AND ur.createdTS<='2012-07-31 23:95:59' 
2

他們是派生查詢,但我認爲他們沒有理由。

例如,考慮到每行中的reportId總是相同的。那麼總是參考驅動表的reportId(MySQL應該足夠聰明以自己做到這一點)是有利的。

例如,a和b表可以加入這樣

FROM 
T_UserReport AS ur 
JOIN T_ReportMonitor AS a ON (a.reportId = ur.reportId AND a.status = 'EndSP') 
JOIN T_ReportMonitor AS b ON (b.reportId = ur.reportId AND b.status = 'BeginSP') 

T_ReportMonitor需要那麼只有一個索引上statusreportId

CREATE INDEX ut_ndx ON T_ReportMonitor (status, reportId, createdTS) 

這允許MySQL來立即選擇EndSP條目爲a,併爲JOIN提供reportId列;這樣做後,它也發現自己與查詢創建的TS。 (更大的)數據表本身根本不需要被訪問。

相同的概念適用於其他表格。如果您

  • JOIN上COLUMN1,
  • 簡單凡COLUMN2篩選子句[AND column2a ...]
  • 只需要在查詢體欄3
  • 和該表明顯大於迄今爲止命名的三列,

那麼你會發現有利於做

JOIN表作爲別名ON(alias.column1 = ... AND alias.column2 = '過濾器值')

,並有這樣一個指數

CREATE INDEX table_ndx ON table (column2, column1, column3)