MySQL是否消除了SELECT和HAVING / GROUP BY子句之间的公共子表达式

时间:2014-05-23 08:33:48

标签: mysql group-by query-optimization having

我经常看到人们通过这样的查询回答MySQL问题:

SELECT DAY(date), other columns
FROM table
GROUP BY DAY(date);

SELECT somecolumn, COUNT(*)
FROM table
HAVING COUNT(*) > 1;

我总是希望为列添加别名,并参考GROUP BYHAVING子句中的别名,例如

SELECT DAY(date) AS day, other columns
FROM table
GROUP BY day;

SELECT somecolumn, COUNT(*) AS c
FROM table
HAVING c > 1;

MySQL是否足够聪明,注意到后面的子句中的表达式与SELECT中的表达式相同,只执行一次?我不确定如何测试这一点 - EXPLAIN并没有显示出任何差异,但它似乎并没有显示它是如何进行分组或过滤的第一名;它似乎主要用于优化连接和WHERE子句。

我倾向于对MySQL优化感到悲观,所以我想尽我所能给予它。

2 个答案:

答案 0 :(得分:5)

我认为这可以使用sleep()函数测试,
例如,看一下这个演示:http://sqlfiddle.com/#!2/0bc1b/1

Select * FROM t;

| X |
|---|
| 1 |
| 2 |
| 2 |

SELECT x+sleep(1)
FROM t
GROUP BY x+sleep(1);

SELECT x+sleep(1) As name
FROM t
GROUP BY name;

两个查询的执行时间约为3000毫秒(3秒) 表中有3条记录,对于每条记录,查询仅休眠1秒,
所以这意味着表达式只针对每条记录评估一次,而不是两次。

答案 1 :(得分:1)

在咨询了一位MySQL工程师之后,我提出了这个冗长的答案。

  • 缓存 - 无法记住查询的任何部分'供以后在该(或后续)查询中使用。 (对比:查询缓存。)
  • 常见的子表达式消除 - 没有。这是一种常见的编译器技术,但MySQL不使用它。示例:(a-b)*(a-b)将执行两次减法。
  • 从循环中删除常量 - 是的,有限制。这是另一种编译器技术。
  • 各种以SQL为中心的黑客 - 是的;见下文。
  • 重新评估子查询 - 这取决于。此外,优化器正逐渐变得更好。
  • VIEWs - 这取决于。仍然存在VIEW注定比同等SELECT更差的情况。示例:无条件下推到UNION中的VIEW。实际上,这更多是延迟行动的问题。
  • 我认为一些较新版本的MariaDB有一个"子查询缓存"。

(警告:我对我的任何答案都没有100%的信心,但我相信大部分都是正确的,如MySQL 5.7,MariaDB 10.1等)

将多行SELECT视为循环。许多,也许全部,"确定性"表达式被评估一次。示例:常量日期表达式,甚至涉及函数调用。但...

NOW()在查询开始时专门评估一次。此外,复制时该值将传递给Slaves。也就是说,当查询存储在从属服务器上时,NOW()可能已过期。 (SYSDATE()是另一种动物。)

特别是only_full_group_by的出现,GROUP BY需要知道它是否与SELECT表达式匹配。所以,这会查找类似的代码。

HAVINGORDER BY可以使用SELECT列表中的别名(与WHEREGROUP BY不同)。因此,SELECT expr AS x ... HAVING expr似乎会重新评估expr,但SELECT expr AS x ... HAVING x似乎已达到已评估的expr

MariaDB 10.2的Windowing功能对它们可以/不能重用的位置有一些非常严格的限制;我还没有完整的图片。

一般来说,这一切都不重要 - 对表达式(DATE(date)甚至COUNT(*))的重新评估会得到相同的答案。此外,通过行的翻找通常比表达评估昂贵得多。所以,除非你有一个好的秒表,否则你不会说出差异。