несколько скользящих средних в MySQL - PullRequest
0 голосов
/ 30 сентября 2018

Мне нужно рассчитать скользящие средние (с разными периодами) для моего набора данных в MySQL.Я попробовал 2 способа вычисления средних значений, но оба требуют значительного количества времени.Совместное использование кода ниже.

Метод: -1

select t1.*, 
    (select avg(t2.last_price) 
        from temp_data t2 
        where t2.rownum>t1.rownum-50 and t2.rownum<=t1.rownum and t1.script_code=t2.script_code) as 'ma_small_price'
from temp_data t1;

Метод: -2

select t1.*, avg(t2.last_price) 'ma_small_price'
from temp_data t1
join temp_data t2
where t2.rownum>t1.rownum-50 and t2.rownum<=t1.rownum and t1.script_code=t2.script_code
group by t1.id,t1.date, t1.time;

Это структура таблицы:

  CREATE TABLE `temp_data` (
  `id` int(11) NOT NULL DEFAULT '0',
  `rownum` int(11) DEFAULT NULL,
  `script_code` float DEFAULT NULL,
  `date` date DEFAULT NULL,
  `time` time DEFAULT NULL,
  `last_price` float DEFAULT NULL,
  `last_qty` float DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

rownum - это столбец с последовательными номерами строк.ID был первичным ключом, но не был последовательным, поэтому мне пришлось добавить отдельный столбец

Ссылка для данных примера: https://www.dropbox.com/s/z8iacqvlkjdx6ax/temp_data_sample.xlsx?dl=0

Далее мне нужно вычислить несколько скользящих средних для одного и того жеданные параллельно , но периоды (указанные в приведенном выше коде как 50) различны.

Мой набор данных огромен и растет (> 1 млн строк) и время, которое требуется для этих запросовбежать важно - ~ 20 минут каждый.Поиск входных данных о том, как эти запросы могут быть улучшены для сокращения времени выполнения.Спасибо !!

Ответы [ 2 ]

0 голосов
/ 30 сентября 2018

Хорошо.Прежде всего, только с 1М строк это не должно занимать 20 минут.Больше похоже на 20 секунд.Если ваш столбец rownum уникален, он должен быть проиндексирован как уникальный ключ.Это также должно быть без знака int.Выполнение этих действий значительно сократит время вашего запроса, потому что кажется, что сейчас вы выполняете полное сканирование несортированной таблицы для каждого соединения.

Во-вторых, если нет какой-либо причины, которая не очевидна дляБаза данных, которая сравнивает много исторических данных, вы должны использовать таблицу ISAM, а не InnoDB.

В-третьих, код_скрипта должен быть проиндексирован, иначе вы будете выполнять полное сканирование таблицы.

Подробнее: * Ваш оператор соединения в методе 2 объединяет каждую строку с каждой строкой и затем выполняет оператор where.Вы должны оставить левое соединение с rownum> t1.rownum-50 и rownum <= t1.rownum, вместо того, чтобы делать общее соединение и затем запускать where.Это значительно ускорит запрос даже без индексации rownum.* Если вы ожидаете намного больше данных, вам также следует изучить разбиение таблицы на основе rownum.Разделы могут быть превосходными для ускорения такого рода операций чтения, когда большая часть данных, к которым вы обращаетесь, является последовательной и попадает в один или два раздела.В вашем случае вы также можете разделить по дате, что может быть полезно для других операций.* Посмотрите на EXPLAIN SELECT и посмотрите, какие ключи используются в соединениях.Рассмотрим подсказку USE INDEX для использования rownum вместо первичного ключа для объединения. </p>

По-видимому, ни один из ваших запросов неверен.После того, как вы выполнили вышеуказанные оптимизации, я предполагаю, что ваш метод-1 (подзапрос) все равно будет работать быстрее, чем правильный JOIN ON без WHERE в методе-2.

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

0 голосов
/ 30 сентября 2018

Хороший вопрос Задача состоит в том, чтобы сгруппировать по итерациям каждую строку с переходом. Поэтому нам нужно определить начальный период и конечный период и объединить одну таблицу между этими периодами

Я добавил порядок по и ограничилиз-за размера таблицы

я бы также добавил индексы в столбец rownum, чтобы объединение и группа работали быстрее

надеюсь, что это поможет

ALTER TABLE temp_data ADD key rownum (rownum) ;


    SELECT 
         t3.rownum AS endp, 
        AVG(t3.last_price)
        FROM
temp_data t3
INNER JOIN temp_data t ON t.rownum BETWEEN  MAX(IFNULL(t3.rownum, 0)) - 50 AND t3.endp
        GROUP BY
        endp
ORDER BY rownum DESC
LIMIT 0,1000
...