Усреднение гранулярных образцов самым простым способом на SQL - PullRequest
0 голосов
/ 15 мая 2018

Итак, у меня есть довольно большой стол с детализированными ценовыми тиками (с использованием MariaDB).

CREATE TABLE `table` (
 `num` int(11) NOT NULL AUTO_INCREMENT,
 `datetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
 `pairs` varchar(40) NOT NULL,
 `price` decimal(16,10) NOT NULL,
 `volume` decimal(22,10) NOT NULL,
 PRIMARY KEY (`num`),
 KEY `datetime_pairs` (`pairs`,`datetime`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1

Я хочу сделать среднечасовое значение этих цен по типу входа, если данные старше x дней. Для этого примера я возьму 7 дней. Я пришел с этим запросом.

SELECT `num`, `datetime`, `pairs`, `price`, `volume`, 
    AVG(`price`) AS `priceAVG`, AVG(`volume`) AS `volumeAVG`
FROM table
WHERE DATE_FORMAT(`datetime`, '%Y-%m-%d %H:00:00') 
    < DATE_FORMAT(DATE_SUB(NOW(),INTERVAL 7 DAY), '%Y-%m-%d %H:00:00')
GROUP BY DATE_FORMAT(`datetime`, '%Y-%m-%d %H:00:00'), `pairs`

Выполнение запроса занимает ~ 25 секунд. Я не думаю, что смогу оптимизировать это. Это МОЖЕТ напечатать результат, который я ищу ... однако я действительно не уверен, что лучше всего отвечать на мой вопрос, как только у меня будут данные для работы.

Вставить этот результат и удалить старые данные? Это могло бы испортить номер первичного ключа num, что сделало бы его несовместимым с порядком datetime.

Обновление старых данных с использованием результата запроса SELECT и удаление старых данных без этих обновленных строк? Вот чего я сейчас пытаюсь достичь ...

Я думаю, что иметь дубликаты таблицы, возможно, не вариант, поскольку у меня есть сто таких таблиц для обработки, и ресурсы ЦП также должны быть рассмотрены. Я использую cron и php для приведения этих запросов. Я мог бы выполнять эту операцию каждые 12 или 24 часа.

Какой метод подойдет в этом случае?

Является ли запрос на обновление реалистичным способом обработки этого?

Ответы [ 3 ]

0 голосов
/ 15 мая 2018

Вам, кажется, тоже нужен час, а не только дата? Так что сравнение части даты не поможет вам, я думаю.

  1. Добавление постоянного сгенерированного столбца , который дает дату с часом, но без минут или секунд (они установлены в 0) из datetime.

    ALTER TABLE `table`
                ADD (`date_to_the_hour` date AS (date(`datetime`) + INTERVAL hour(`datetime`) HOUR) PERSISTENT);
    
  2. Поместите индекс в этот столбец и pairs.

    CREATE INDEX `date_to_the_hour_pairs`
                 ON `table`
                    (`date_to_the_hour`,
                     `pairs`);
    
  3. Измените свой выбор, чтобы сравнить с новым столбцом.

    SELECT `num`,
           `datetime`,
           `pairs`,
           `price`,
           `volume`, 
           AVG(`price`) `priceAVG`,
           AVG(`volume`) `volumeAVG`
           FROM `table`
           WHERE `date_to_the_hour` < date(now() - INTERVAL 7 DAY) + INTERVAL hour(now()) HOUR
           GROUP BY `date_to_the_hour`,
                    `pairs`;
    

Это может немного ускорить процесс.

0 голосов
/ 23 мая 2018
  • Любая причина использовать 11-байтовый decimal(22,10) вместо простого 4-байтового FLOAT?

  • Не имеет смысла выбиратьnum, datetime, pairs, price, volume при выполнении GROUP BY.

  • Создание и ведение сводной таблицы с разбивкой по часам.Затем создайте отчет из этой таблицы.http://mysql.rjweb.org/doc.php/summarytables

  • Есть ли изменения в двух записях с одним и тем же комбо (datetime, pairs)?Если не избавиться от id и сделать из этого 2 столбца PRIMARY KEY.

  • Тем временем вы могли бы частично оптимизировать свой запрос

для среднечасовых значений за последнюю неделю до текущего часа, но не включая текущий час:

SELECT  LEFT(`datetime`, 13) AS the_hour,
        pairs,
        AVG(`price`)  AS `priceAVG`,
        AVG(`volume`) AS `volumeAVG`
    FROM `table`
    WHERE `datetime` >= DATE_FORMAT(NOW() - INTERVAL 7 DAY), '%Y-%m-%d %H:00:00')
      AND `datetime`  < DATE_FORMAT(NOW(), '%Y-%m-%d %H:00:00')
    GROUP BY LEFT(`datetime`, 13), pairs

и

INDEX(datetime)

Подход с использованием сводной таблицы является наиболее сложным, но дает ваммаксимальная отдача.

0 голосов
/ 15 мая 2018
  1. Преобразование дат в строки с DATE_FORMAT() неэффективно.Сравните даты напрямую.
  2. Сравнение строк неэффективно.Сравните даты напрямую.
  3. У вас нет индекса в поле datetime.Нет, datetime_pairs не считается, потому что:
    • pairs не участвует.
    • datetime не первый элемент в составном ключе.

Итак:

CREATE TABLE `table` (
 `num` int(11) NOT NULL AUTO_INCREMENT,
 `datetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
 `pairs` varchar(40) NOT NULL,
 `price` decimal(16,10) NOT NULL,
 `volume` decimal(22,10) NOT NULL,
 PRIMARY KEY (`num`),
 KEY `datetime` (`datetime`), -- change
 KEY `pairs` (`pairs`)        -- change
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1

И:

SELECT `num`, `datetime`, `pairs`, `price`, `volume`, 
    AVG(`price`) AS `priceAVG`, AVG(`volume`) AS `volumeAVG`
FROM table
-- DATE(datetime) is the same as 00:00:00 on that day, but is
-- a DATE type that can be efficiently compared
WHERE `datetime` < DATE(DATE_SUB(NOW(),INTERVAL 7 DAY)
GROUP BY DATE(`datetime`), `pairs`

И вы все еще будете ограничены GROUP BY DATE(datetime) потому что нет индекса для использования.Возможно, вы захотите добавить столбец anoter, который просто date DATE NOT NULL, чтобы вы могли использовать индекс для него, хотя приведенное выше уже должно значительно сократить требуемое время.

Кроме того, столбцы num, datetime, price, volume в запросебудет в значительной степени бессмысленным, так как они не упоминаются в операторе GROUP BY.

Кроме того, исторические данные не меняются, поэтому вам никогда не придется собирать ежедневные данные более одного раза.Хранения ежедневных агрегатов, таких как среднее значение и число, будет достаточно для построения более крупных агрегатов сверхуНапример:

SELECT AVG(daily_count * daily_average) AS 'weekly_average'
FROM daily_aggregates
WHERE datestamp > DATE(NOW() - INTERVAL 7 DAY)

Использование кэширования.Я видел много проблем с производительностью из-за того, что разработчики многократно выполняли одни и те же вычисления для гигантских исторических наборов данных, которые никогда не изменятся.Простое кэширование ежедневных агрегатов может сократить набор данных с тысяч или миллионов элементов в год до 365.

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