Это еще один подход.
Этот запрос будет испытывать те же проблемы с производительностью, что и другие запросы, которые возвращают правильные результаты, поскольку для плана выполнения этого запроса потребуется операция SORT в КАЖДОЙ строке таблицы статистики. Поскольку в столбце времени нет предиката (ограничения), будет рассмотрена КАЧАЯ строка в таблице статистики. Для ДЕЙСТВИТЕЛЬНО большого стола stats
это сдует все доступное временное пространство, прежде чем оно умрет от ужасной смерти. (Другие заметки о производительности ниже.)
SELECT r.*
, IFNULL(s.avg_votes,0)
FROM servers r
LEFT
JOIN (SELECT t.server
, AVG(t.votes) AS avg_votes
FROM (SELECT CASE WHEN u.server = @last_server
THEN @i := @i + 1
ELSE @i := 1
END AS i
, @last_server := u.server AS `server`
, u.votes AS votes
FROM (SELECT @i := 0, @last_server := NULL) i
JOIN (SELECT v.server, v.votes
FROM stats v
ORDER BY v.server DESC, v.time DESC
) u
) t
WHERE t.i <= 24
GROUP BY t.server
) s
ON s.server = r.id
Что этот запрос делает сортирует таблицу статистики, сервер и убывающего порядок по столбцу времени. (Inline view aliased as u
.)
С отсортированным набором результатов мы присваиваем номера строк 1,2,3 и т. Д. Каждой строке для каждого сервера. (Inline view aliased как t
.)
С этим набором результатов мы отфильтровываем любые строки с номером rownumber> 24 и вычисляем среднее значение столбца votes
для «последних» 24 строк для каждого сервера. (Inline view aliased as s
.)
В качестве последнего шага мы присоединяем это к таблице серверов, чтобы вернуть запрошенный набор результатов.
Примечание:
План выполнения этого запроса будет затратным для большого количества строк в таблице stats
.
Для улучшения производительности есть несколько подходов, которые мы могли бы предпринять.
Простейший может включать в запрос предикат EXCLUDES значительное количество строк из таблицы stats
(например, строки с time
значениями старше 2 дней или старше 2 недель). Это значительно сократило бы количество строк, которые нужно отсортировать, чтобы определить «последние» 24 строки.
Кроме того, с индексом на stats(server,time)
также возможно, что MySQL может сделать относительно эффективное «обратное сканирование» по индексу, избегая операции сортировки.
Мы также можем рассмотреть возможность применения индекса в таблице статистики по (server,"reverse_time")
. Поскольку MySQL еще не поддерживает нисходящие индексы, реализация будет действительно регулярным (восходящим) индексом для производного значения rtime
(выражение «обратного времени», которое возрастает для нисходящих значений time
(например, -1*UNIX_TIMESTAMP(my_timestamp)
или -1*TIMESTAMPDIFF('1970-01-01',my_datetime)
.
Другим подходом к повышению производительности будет сохранение теневой таблицы, содержащей самые последние 24 строки для каждого сервера. Это было бы проще реализовать, если мы можем гарантировать, что «последние строки» не будут удалены из таблицы stats
Мы можем сохранить эту таблицу с помощью триггера. В принципе, всякий раз, когда строка вставляется в таблицу stats
, мы проверяем, будет ли time
в новых строках позже, чем самый ранний time
, сохраненный для сервера в тени table, если это так, мы заменим самую раннюю строку в теневой таблице новой строкой, не забудьте сохранить не более 24 строк в теневой таблице для каждого сервера.
И еще один подход - написать процедуру или функцию, которые получают результат. Подходом здесь будет цикл через каждый сервер и запуск отдельного запроса к таблице статистики, чтобы получить среднее значение votes
для последних 24 строк и собрать все эти результаты вместе. (Этот подход действительно может быть скорее обходным путем, чтобы избежать сортировки на огромном временном наборе, просто для того, чтобы вернуть возвращаемый набор результатов, что не обязательно приводило к быстрому возврату результатов.)
Практический результат этот тип запроса в таблице LARGE ограничивает количество строк, рассмотренных в запросе, и исключает операцию сортировки на большом множестве. Вот как мы получаем такой запрос.
ДОПОЛНЕНИЕ
Для того, чтобы получить «обратное сканирование индекса» операцию (чтобы получить строки из stats
заказанных с использованием индекса без операции FileSort), я должен указать DESCENDING на оба выражениях в Предложение ORDER BY. Ранее запрос состоял из ORDER BY server ASC, time DESC
, и MySQL всегда хотел сделать fileort, даже указав подсказку FORCE INDEX FOR ORDER BY (stats_ix1)
.
Если требование заключается в возврате «среднего голоса» для сервера только, если в таблице статистики имеется не менее 24 связанных строк, тогда мы можем сделать более эффективный запрос, даже если он немного больше беспорядочный. (Большая часть беспорядка во вложенных функциях IF() заключается в том, чтобы иметь дело со значениями NULL, которые не включаются в среднее значение. Это может быть намного менее беспорядочно, если у нас есть гарантия, что votes
не является NULL, или если мы исключаем любые строки, в которых votes
равно нулю.)
SELECT r.*
, IFNULL(s.avg_votes,0)
FROM servers r
LEFT
JOIN (SELECT t.server
, t.tot/NULLIF(t.cnt,0) AS avg_votes
FROM (SELECT IF(v.server = @last_server, @num := @num + 1, @num := 1) AS num
, @cnt := IF(v.server = @last_server,IF(@num <= 24, @cnt := @cnt + IF(v.votes IS NULL,0,1),@cnt := 0),@cnt := IF(v.votes IS NULL,0,1)) AS cnt
, @tot := IF(v.server = @last_server,IF(@num <= 24, @tot := @tot + IFNULL(v.votes,0) ,@tot := 0),@tot := IFNULL(v.votes,0) ) AS tot
, @last_server := v.server AS SERVER
-- , v.time
-- , v.votes
-- , @tot/NULLIF(@cnt,0) AS avg_sofar
FROM (SELECT @last_server := NULL, @num:= 0, @cnt := 0, @tot := 0) u
JOIN stats v FORCE INDEX FOR ORDER BY (stats_ix1)
ORDER BY v.server DESC, v.time DESC
) t
WHERE t.num = 24
) s
ON s.server = r.id
с индексом покрытия на stats(server,time,votes)
, Объяснить показал, MySQL, избежать операции FileSort, поэтому он должен быть использован «обратного сканирования индекса» для возврата строки в порядке. Отсутствует индекс покрытия и индекс на '(сервер, время) , MySQL used the index if I included an index hint, with the
FORCE INDEX FOR ORDER BY (stats_ix1) `подсказка, MySQL также избегал файлового массива. (Но поскольку моя таблица имела менее 100 строк, я не думаю, что MySQL уделяет много внимания тому, чтобы избежать операции с файловым файлом.)
Выражения времени, голосов и выражения avg_sofar (во встроенном представлении aliased как t
); они не нужны, но они предназначены для отладки.
Способ, которым этот запрос стоит, для каждого сервера требуется не менее 24 строк в статистике, чтобы вернуть среднее значение. (Это может быть приемлемо.) Но я думал, что в целом мы можем вернуть общее количество, общее количество (tot) и счетчик операций (cnt).
(Если заменить WHERE t.num = 24
с WHERE t.num <= 24
, мы можем видеть, скользящее среднее в действии.)
Для возврата в среднем, где нет, по крайней мере 24 строк в статистике, это действительно вопрос, идентифицирующая (для каждого сервера) с максимальным значением num, которое равно < = 24.
Я считаю, что [это] (http://sqlfiddle.com/#!2/d908f/5) является структурой таблицы вашей таблицы , Правильно? –