优化SQL查询

时间:2010-02-15 08:40:37

标签: mysql join

我正在尝试优化此慢查询(> 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秒)。

5 个答案:

答案 0 :(得分:6)

WHERE条款中最具选择性的部分是什么?也就是说,哪个条件从结果集中删除了最可能的项目?

我猜这是mtu.ts过滤器。如果这是真的,您还应该对mtu.ts列进行索引,并尝试以可以使用索引的方式对此进行约束;例如,使用BETWEEN运算符。

其他提示:

  • 将联接子句直接附加到JOIN ... ON ()的联接,这使得查询更容易阅读,无论是人类还是优化器
  • 避免在查询中计算常量,例如YEAR(NOW())
  • 避免WHERE子句中所选列的功能,如MONTH(mtu.ts)。这减少了大量使用指数的可能性。
  • 规范化您的数据以避免像mtu.dept = 'GUN' OR mtu.dept = 'gun'这样的套管问题;表格中的单个UPDATE mtu SET dept = lower(dept)和适当的CHECK dept = lower(dept)将有助于避免这种疯狂。

答案 1 :(得分:2)

  1. 我会使用连接重写查询。它更清晰,给予优化者更好的机会。
  2. 月(mtu.ts)= 2和年(mtu.ts)=年(现在()) - 更好地使用..和..之间的mtu.ts。

答案 2 :(得分:0)

您可以将文字字符串更改为数字吗?

答案 3 :(得分:0)

我能看到的最明显的解决方案是将COUNT(*)更改为仅涵盖一个字段名称,否则您的索引可能会无用!

答案 4 :(得分:0)

作为一般原则,分析此类问题的一个好方法是了解您匹配的数据,以了解其基数。

也就是说,订购您的查询,以便首先发生最具选择性的事情。 你的数据中更有可能是dept ='GUN'或者userId是28。

最后,你有没有考虑加入MT和MTU而不是过滤? 它可能会使您的查询更快,因为您将限制需要日期比较的数据量。