需要优化MySQL查询

时间:2012-10-10 06:29:18

标签: mysql

我有一个33秒的查询是否有更好的方法来重写它,如何将其转换为过程

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子句和任何其他比较中使用了列上的键,但在查询中没有使用它们,是因为它们是派生查询。 < / p>

2 个答案:

答案 0 :(得分: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立即为a选择EndSP条目,并将reportId列用于JOIN;完成后,它还会发现自己使用查询的createdTS。根本不需要访问(大得多)数据表本身。

相同的概念适用于其他表。如果你

  • 加入第1列,
  • 在column2上有简单 WHERE过滤器子句[AND column2a ...]
  • 只需要查询正文中的column3
  • 并且该表明显大于目前为止命名的三列

然后你会发现有利于做

JOIN表AS别名ON(alias.column1 = ... AND alias.column2 ='filter value')

并有一个索引,如

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

答案 1 :(得分: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上有一个键。

还有一件事会降低您的查询质量。您在where:

中使用了一个函数
date(ur.createdTS)

这意味着MySQL必须处理每一行以查看此函数的结果。这甚至可能是最大的性能提升。尝试将该字段设为日期字段(或创建新的日期字段)或使用类似

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