我的查询看起来像这样:
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专业人员,我可以对我的表做些什么样的优化,以便更快地完成。
这里使用的最佳技术是什么
快速分组
WHERE更快(索引?分区?)
情况更快(我认为是主要的减速)。
关于数据的一些见解:
大约5密耳的记录,
mainname
是具有最多重复值的列。因此,对于5种类型的mainname
,可能是1mil。
在这1mil之内,我有大约90个日期的范围,我正在执行BETWEEN语句。
同样在每个1mil内,有MAX 4-5不同cat2
。所以可能还有'x','y'或者'z''z1',但不是更多。我有额外的总和(当......语句对应于cat2
s的类型数时。
换句话说,我需要每种cat2
的分组总和。
非常感谢。
答案 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”。
(我在这里假设对somedate
,someotherdate
和somename
的引用是对语句中提供的文字值的引用,而不是列引用。)
如果返回的行数非常小(与表的大小相比),那么您可以尝试创建一个以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))
(但这不太可能有任何变化表现。)