MySQL:加快包含子查询的查询

时间:2018-10-03 09:34:01

标签: mysql sql

表格

id | date | id_device | total | others 15/20 columns
----------------------------------------------------
  • 可以包含数百万条记录
  • 该表具有 id_device date 列的索引。
  • 这是一张表格,每分钟X能源设备会节省其能耗(总列。该值一直在增加)。但也可能会有空的时间间隔,如下表所示。

我需要计算特定时间间隔内某天特定设备的每小时消耗量。

为此,我有一个查询,有效。 例如:2018-10-03,间隔时间为00-01。此间隔意味着(以及所有其他间隔)从00之前的最后一条记录开始,一直到00的最后一条记录结束。因此,像上面的示例一样,00间隔的总数为300-120, 300 (最后一条记录为00) 120 (最后一条记录在00之前)。 减法是在PHP中完成的。

id | date                | id_device | total | others 15/20 columns
----------------------------------------------------
1  | 2018-10-02 23:50:20 | 1         | 100   | ....
2  | 2018-10-02 23:55:20 | 1         | 120   | ....
3  | 2018-10-03 00:01:20 | 1         | 150   | ....
.. | 2018-10-03 00:59:20 | 1         | 300   | ....
.. | 2018-10-03 01:00:20 | 1         | 350   | ....

SELECT `total` AS `total` FROM `mytable` AS `A`, 
    (
        SELECT MIN(`date`) AS `firstValue`, MAX(`date`) AS `lastValue`
        FROM `mytable`
        WHERE `date` BETWEEN 
        COALESCE((SELECT `date` FROM `mytable` WHERE `date` < '2018-10-03 00:00:00' AND `id_device` = 1 ORDER BY `date` DESC LIMIT 1), '2018-10-03 00:00:00'
        AND '2018-10-03 00:59:59'
        AND `id_device` = 1
    ) AS `B`

    WHERE `A`.`date` IN (`B`.`firstValue`,`B`.`lastValue`) AND `id_device` = 1
    ORDER BY `A`.`date`

使用此查询,执行时间约为 0.9 / 1.5秒。而且太慢了(我必须为每个设备循环计算X次此查询)。

删除子查询,执行时间为实际上为0 。执行时间是完美的,但是这种方式的查询显然不适合我。

SELECT `total` AS `total` FROM `mytable` AS `A`, 
    (
        SELECT MIN(`date`) AS `firstValue`, MAX(`date`) AS `lastValue`
        FROM `mytable`
        WHERE `date` BETWEEN 
        '2018-10-03 00:00:00'
        AND '2018-10-03 00:59:59'
        AND `id_device` = 1
    ) AS `B`

    WHERE `A`.`date` IN (`B`.`firstValue`,`B`.`lastValue`) AND `id_device` = 1
    ORDER BY `A`.`date`

我分别测试了子查询,执行时间为实际上为0

SELECT `date` FROM `mytable` WHERE `date` < '2018-10-03 00:00:00' AND `id_device` = 1 ORDER BY `date` DESC LIMIT 1

所以我不明白为什么原始查询这么慢。

3 个答案:

答案 0 :(得分:0)

我认为,如果您可以在

中逻辑上设置较低的日期范围(例如5天前或30天前,取决于您的问题)
SELECT `date` FROM `mytable` WHERE `date` < DATE_FORMAT('2018-10-03 00:00:00', '%Y-%m-%d %H:%i:%s') AND `id_device` = 1 ORDER BY `date` DESC LIMIT 1

可以在合理的时间内得到响应

答案 1 :(得分:0)

反转查询的层次结构(使外部查询成为子查询),或者最好还是使用联接。 如果可能的话,在加入和过滤时,尝试(显然是船长)使用索引字段。 在变量中为where设置日期,并使用变量代替dateformat。否则,实际上将为每一行计算该查询,这会大大降低查询速度。

答案 2 :(得分:0)

我将从将子查询移至FROM子句开始:

SELECT `total` AS `total`
FROM `mytable` AS `A`CROSS JOIN 
     (SELECT MIN(t2.`date`) AS `firstValue`, MAX(t2.`date`) AS `lastValue`
      FROM `mytable` t2 CROSS JOIN
           (SELECT t3.`date`
            FROM `mytable` t3
            WHERE t3.`date` < '2018-10-03' AND t3.`id_device` = 1
            ORDER BY t3.`date` DESC
            LIMIT 1
           ) d
      WHERE t2.date >= COALESCE(d.date, '2018-10-03') AND
            t2.date < '2018-10-04' AND
            t2.id_device = 1
    ) B
WHERE `A`.`date` IN (B.firstValue, B.lastValue) AND
      A.`id_device` = 1
ORDER BY `A`.`date`;

对于此查询,我将从mytable(id_device, date)上的索引开始。

我也建议使用索引,但是您指出基本查询运行很快。因此,不需要其他索引。

您可能还可以使用union all简化逻辑。