我正在尝试优化此慢查询(> 2s)
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),但是对于1,380,384条记录的数据集运行查询仍需要2秒多的时间。是否有另一种以最佳方式表达此查询的方式?
更新:使用David的建议,现在查询时间缩短到几毫秒,而不是运行超过2秒(实际上,MySQL 5.0版本为51秒)。
答案 0 :(得分:6)
WHERE
条款中最具选择性的部分是什么?也就是说,哪个条件从结果集中删除了最可能的项目?
我猜这是mtu.ts
过滤器。如果这是真的,您还应该对mtu.ts
列进行索引,并尝试以可以使用索引的方式对此进行约束;例如,使用BETWEEN
运算符。
其他提示:
JOIN ... ON ()
的联接,这使得查询更容易阅读,无论是人类还是优化器YEAR(NOW())
MONTH(mtu.ts)
。这减少了大量使用指数的可能性。mtu.dept = 'GUN' OR mtu.dept = 'gun'
这样的套管问题;表格中的单个UPDATE mtu SET dept = lower(dept)
和适当的CHECK dept = lower(dept)
将有助于避免这种疯狂。答案 1 :(得分:2)
答案 2 :(得分:0)
您可以将文字字符串更改为数字吗?
答案 3 :(得分:0)
我能看到的最明显的解决方案是将COUNT(*)更改为仅涵盖一个字段名称,否则您的索引可能会无用!
答案 4 :(得分:0)
作为一般原则,分析此类问题的一个好方法是了解您匹配的数据,以了解其基数。
也就是说,订购您的查询,以便首先发生最具选择性的事情。 你的数据中更有可能是dept ='GUN'或者userId是28。
最后,你有没有考虑加入MT和MTU而不是过滤? 它可能会使您的查询更快,因为您将限制需要日期比较的数据量。