我有一个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>
答案 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
只需要status
和reportId
上的索引:
CREATE INDEX ut_ndx ON T_ReportMonitor ( status, reportId, createdTS )
这允许MySQL立即为a选择EndSP条目,并将reportId列用于JOIN;完成后,它还会发现自己使用查询的createdTS。根本不需要访问(大得多)数据表本身。
相同的概念适用于其他表。如果你
然后你会发现有利于做
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'