没有附加列的子查询比有列的子查询花费更长的时间

Posted

技术标签:

【中文标题】没有附加列的子查询比有列的子查询花费更长的时间【英文标题】:Subquery without additional column takes longer than with column 【发布时间】:2016-07-14 15:35:10 【问题描述】:

我正在尝试使用子查询获取运行总计。 (我正在使用 Metabase,它似乎不接受/处理查询中的变量)

我的查询:

SELECT date_format(t.`session_stop`, '%d') AS `session_stop`, 
    sum(t.`energy_used` / 1000) AS `csum`,
    (
      SELECT (SUM(a.`energy_used`) / 1000)
      FROM `sessions` a 
      WHERE date_format(a.`session_stop`, '%Y-%m-%d') <=  date_format(t.`session_stop`, '%Y-%m-%d') 
      AND str_to_date(concat(date_format(a.`session_stop`, '%Y-%m'), '-01'), '%Y-%m-%d') = str_to_date(concat(date_format(now(), '%Y-%m'), '-01'), '%Y-%m-%d')
      ORDER BY str_to_date(date_format(a.`session_stop`, '%e'), '%d') ASC
    ) AS `sum`
    FROM `sessions` t
    WHERE str_to_date(concat(date_format(t.`session_stop`, '%Y-%m'), '-01'), '%Y-%m-%d') = str_to_date(concat(date_format(now(), '%Y-%m'), '-01'), '%Y-%m-%d')
    GROUP BY date_format(t.`session_stop`, '%e')
    ORDER BY str_to_date(date_format(t.`session_stop`, '%d'), '%d') ASC;

运行大约需要 1.29 秒。 (总共 43K 行,返回 14)

如果我删除 sum(t.`energy_used` / 1000) AS `csum`, 行,查询将占用 8 分 40 秒。

这是为什么?我宁愿没有那条线,但我也不能等待 8 分钟来处理查询。

(我知道我可以创建一个累积列,但我特别感兴趣的是为什么这个额外的sum() 会加快整个查询的速度)

ps。在 mysql 控制台和 Metabase 界面上对此进行了测试。

解释查询:

+----+--------------------+-------+------+---------------+------+---------+------+-------+---------------------------
| id | select_type        | table | type | possible_keys | key  | key_len | ref  | rows  | Extra
+----+--------------------+-------+------+---------------+------+---------+------+-------+---------------------------
|  1 | PRIMARY            | t     | ALL  | NULL          | NULL | NULL    | NULL | 42055 | Using where; Using tempora
|  2 | DEPENDENT SUBQUERY | a     | ALL  | NULL          | NULL | NULL    | NULL | 42055 | Using where
+----+--------------------+-------+------+---------------+------+---------+------+-------+---------------------------
2 rows in set (0.00 sec)

没有多余的sum():

+----+--------------------+-------+------+---------------+------+---------+------+-------+----------------------------------------------+
| id | select_type        | table | type | possible_keys | key  | key_len | ref  | rows  | Extra                                        |
+----+--------------------+-------+------+---------------+------+---------+------+-------+----------------------------------------------+
|  1 | PRIMARY            | t     | ALL  | NULL          | NULL | NULL    | NULL | 44976 | Using where; Using temporary; Using filesort |
|  2 | DEPENDENT SUBQUERY | a     | ALL  | NULL          | NULL | NULL    | NULL | 44976 | Using where                                  |
+----+--------------------+-------+------+---------------+------+---------+------+-------+----------------------------------------------+
2 rows in set (0.00 sec)

Schema 只不过是一个包含以下内容的表:

session_id (INT, auto incr., prim.key) | session_stop (datetime) | energy_used (INT) |
 1                           | 1-1-2016 10:00:00       | 123456            |
 2                           | 1-1-2016 10:05:00       | 123456            |
 3                           | 1-2-2016 10:10:00       | 123456            |
 4                           | 1-2-2016 12:00:00       | 123456            |
 5                           | 3-3-2016 14:05:00       | 123456            |

互联网上的一些示例显示将 ID 用于 WHERE 子句,但我的结果很差。

【问题讨论】:

通过Explain 运行它的高级审查可能会有所帮助。另外,您的架构。 忘了添加 :( 它现在在那里。(我以为我可以在索引上做点什么,但结果并没有真正表明这一点) 你的其他解释显示了什么。你的架构是什么。 第二个的解释输出。好的,如果我有时间,我将创建一个数据集并尝试重新创建它。这一切都需要时间。您想要有关差异或原因的信息,但您只提供了 1 个解释输出。 "Schema 只不过是一个带有:" 但索引是什么? 【参考方案1】:

您的查询根本不相似。事实上,它们是天壤之别。

如果我删除 sum(t.energy_used / 1000) AS csum, 行,查询 耗时 8 分 40 秒。

当您使用 SUM 时,它是一种聚合。 sum(t.energy_used/ 1000) 将产生与仅选择 t.energy_used 完全不同的结果,这就是查询时间有如此巨大差异的原因。

还不清楚您为什么要以这种方式比较日期:

WHERE date_format(a.`session_stop`, '%Y-%m-%d') <=      date_format(t.`session_stop`, '%Y-%m-%d') 

为什么你在比较之前都用 date_format 转换它们?由于两个表显然都包含相同的数据类型,您应该可以使用a.session_stop &lt;= t.session_stop 这两种情况都会快得多。

由于这是一个不等式比较,它不是一个很好的索引候选,但您仍然可以尝试在该列上创建一个索引,看看它是否有任何效果。

总而言之,性能差异是因为您不只是添加/删除额外的列,而是添加/删除聚合。

【讨论】:

以上是关于没有附加列的子查询比有列的子查询花费更长的时间的主要内容,如果未能解决你的问题,请参考以下文章

带有 SQL 注入保护的简单查询比没有的要花费更长的时间

SQL-query 在代码中比直接查询 db 花费更长的时间

使用 C# 中的子查询访问数据库 INSERT

NHibernate 执行简单查询需要更长的时间

如何从 PostgreSQL 的子查询中选择包含值数组的列?

SELECT中(非常)常用的子查询操作