2010-02-15 81 views
4

我试图优化这个慢查询(> 2秒)优化SQL查询

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 ),但仍需要2秒钟才能对1,380,384条记录的数据集运行查询。是否有另一种以最佳方式表达此查询的方式?

更新:使用David的建议,查询现在下降到几毫秒而不是运行超过2秒(实际上,在MySQL 5.0版本上为51秒)。

+0

我会写'WHERE lower(mtu.dept)='gun'AND ...'但我认为你的数据库已经以这种方式优化了它。 – initall

+0

我发现,至少在Oracle中,在查询的lhs中使用较低的值会导致大规模的减速。与其他字符串比较相比是否会导致更慢的速度... –

+1

在列上使用lower()是不使用任何索引的好方法。这将解释你的减速。 –

回答

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)将有助于避免这种疯狂。
2
  1. 我会重写查询使用连接。这更清楚并给予优化者更好的机会。
  2. MONTH(mtu.ts)= 2,YEAR(mtu.ts)= YEAR(NOW()) - 之间更好地利用mtu.ts ..
+0

你会如何改写?再次感谢。 – Francis

+1

从crmentityÇSELECT COUNT(*) 内上mtu.trans_code = mt.trans_code 其中 '20100201' 之间mtu.ts和 '20100228' 和上mt.activityid = c.crmid 内部联接mdcalls_trans_activity_update MTU加入mdcalls_trans公吨(mtu.dept in('GUN','gun')and c.deleted = 0 and c.smownerid = 28 – burnall

+0

感谢这个例子。我在PHP中创建了一个函数来获取月份的开始日期和结束日期并在'BETWEEN'声明中使用它。 – Francis

0

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

0

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

0

作为一般原则,分析这类问题的一种好方法是理解您的匹配数据,以欣赏其基数。

也就是说,命令您的查询,以便最有选择性的事情首先发生。 更有可能在您的数据中,该部门='GUN'或userId将是28.

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

+0

发布得太快了,基本上David Schmitt和Burnall在说什么! –