使用CASE WHEN优化查询

时间:2014-08-06 20:23:46

标签: mysql

我的查询看起来像这样:

select cat1, cat3, cat4
       sum(case when cat2='x' then val end) as sumValForX,
       sum(case when cat2='y' then val end) as sumValForY
from your_table WHERE date between somedate and someotherdate AND mainname=somename
group by cat1, cat3, cat4 

基本上这把我的桌子分组在cat1,3,4上;并且当cat2 ='x'和cat2 ='y'时,显示结果组的累计总数

我正在研究一张非常大的桌子(比如500万条记录),所以这需要花费很多时间。这是可行的,但我只是想问一些MySQL专业人员,我可以对我的表做些什么样的优化,以便更快地完成。

这里使用的最佳技术是什么

  1. 快速分组

  2. WHERE更快(索引?分区?)

  3. 情况更快(我认为是主要的减速)。

  4. 关于数据的一些见解:

    大约5密耳的记录,

    mainname是具有最多重复值的列。因此,对于5种类型的mainname,可能是1mil。

    在这1mil之内,我有大约90个日期的范围,我正在执行BETWEEN语句。

    同样在每个1mil内,有MAX 4-5不同cat2。所以可能还有'x','y'或者'z''z1',但不是更多。我有额外的总和(当......语句对应于cat2 s的类型数时。

    换句话说,我需要每种cat2的分组总和。

    非常感谢。

1 个答案:

答案 0 :(得分:1)

CASE表达式不太可能导致“减速”。您可以通过从SELECT列表中删除这些表达式来测试它。)

合适的覆盖索引可能是提高此查询效果的最佳选择。

根据我的经验,对于大型集合上的此类查询, GROUP BY 是最大的减速。我知道使用InnoDB和合适的覆盖索引,我有时可以消除“使用filesort”操作(如EXPLAIN输出中所示).MySQL可以使用具有适当前导列的索引来优化GROUP BY操作,而不是“使用filesort”操作。 date列上的范围谓词(在WHERE子句中)可能是个问题,并且会干扰它。

我们需要运行EXPLAIN来验证。

根据查询,我建议覆盖索引:

... ON your_table (mainname, cat1, cat3, cat4, date, cat2)

首先mainname列(由于WHERE子句中的等式谓词,MySQL可以使用索引范围扫描。)

接下来是GROUP BY子句中的三列(以优化GROUP BY操作)。

接下来是查询中引用的其他列(使其成为“覆盖”索引,而无需引用基础表中的页面。)

我们希望在EXPLAIN输出中看到“使用索引”,而不是“使用临时”而不是“使用filesort”。

(我在这里假设对somedatesomeotherdatesomename的引用是对语句中提供的文字值的引用,而不是列引用。)


如果返回的行数非常小(与表的大小相比),那么您可以尝试创建一个以maindate作为前导列的索引,然后是date列,然后按任何顺序排列其他列。使用该索引,MySQL可以在date列上进行范围扫描,但随后需要执行“filesort”操作来执行GROUP BY。

如果返回行的顺序不重要,您可以尝试添加ORDER BY NULL。我没有看到任何性能提升,但文档表明可能有可能进行优化(可能是在最近或未来的版本中?)


对于大型集合,我的本能就是优化GROUP BY


如果我无法获得良好的执行计划(date列上的谓词可能有问题),我将探索重写语句以将该谓词重定位到CASE表达式的选项:

SUM(CASE WHEN t.date BETWEEN 'foo' AND 'bar' AND t.cat2 = 'x' THEN t.val END)

请注意,这可能会更改结果集,方法是返回未在指定日期范围内显示的(cat1,cat3,cat4)值。如果这是一个问题,我可能会提出一个HAVING条款来消除那些“额外”的行。

如果不考虑可移植性,我可能会选择等效的MySQL IF()表达式

SUM(IF(t.date BETWEEN 'foo' AND 'bar' AND t.cat2 = 'x', t.val, NULL))

(但这不太可能有任何变化表现。)