2012-10-10 161 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)