Mysql: ускорить запрос, который содержит подзапрос - PullRequest
0 голосов
/ 03 октября 2018

Таблица :

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 голосов
/ 03 октября 2018

Измените иерархию запросов (превратите внешнюю в подзапрос), или лучше используйте соединение.Попробуйте (здесь очевидно, капитан) использовать индексированные поля, если это возможно, при объединении и фильтрации.Установите даты для where в переменных и используйте переменные вместо dateformat.В противном случае он будет фактически рассчитан для каждой строки, что может значительно замедлить запрос.

0 голосов
/ 03 октября 2018

Я бы начал с перемещения подзапроса в предложение 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.

0 голосов
/ 03 октября 2018

Я думаю, что если вы можете логически установить нижнюю полосу для даты (например, 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

можно получить ответ в разумные сроки

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...