2

Мне нужно рассчитать недельные и месячные скользящие средние на датчик в день для большого набора данных выборки на основе некоторых критериев качества. У меня есть рабочее решение, основанное на коррелированных подзапросах (или сам присоединяется), но мне было интересно, возможно ли использование аналитических функций и приведет к лучшей производительности?Скользящие средние с использованием аналитических функций в T-SQL SQL Server 2014

Вот что я прямо сейчас (упрощенно):

CREATE TABLE Samples(
    SensorId int, 
    SampleTime datetime, 
    Value float, 
    Quality float 
) 

WITH DailyAvg (SensorId, SampleDate, ValueSum, ValueCount) 
AS 
(
    SELECT 
     SensorId, 
     CAST(SampleTime AS DATE) AS SampleDate, 
     SUM(Value) AS ValueSum, 
     COUNT_BIG(Value) AS ValueCount 
    FROM Samples 
    WHERE Quality > 0.95 
    GROUP BY SensorId, CAST(SampleTime AS DATE) 
) 
SELECT 
    SensorId, 
    SampleDate, 
    (SELECT SUM(d2.ValueSum)/SUM(d2.ValueCount) FROM DailyAvg AS d2 WHERE d2.SensorId = d1.SensorId AND d2.SampleDate BETWEEN DATEADD(DAY, -7, d1.SampleDate) AND d1.SampleDate) AS AverageLastWeek, 
    (SELECT SUM(d2.ValueSum)/SUM(d2.ValueCount) FROM DailyAvg AS d2 WHERE d2.SensorId = d1.SensorId AND d2.SampleDate BETWEEN DATEADD(DAY, -14, d1.SampleDate) AND d1.SampleDate) AS AverageLast2Weeks, 
    (SELECT SUM(d2.ValueSum)/SUM(d2.ValueCount) FROM DailyAvg AS d2 WHERE d2.SensorId = d1.SensorId AND d2.SampleDate BETWEEN DATEADD(MONTH, -1, d1.SampleDate) AND d1.SampleDate) AS AverageLastMonth 
FROM DailyAvg d1 
ORDER BY SensorId, SampleDate 

Я попытался заменить суб-запроса для еженедельного среднего с ниже фрагмент кода, но это, очевидно, не может обрабатывать дней без каких-либо образцов правильно. Я думал об использовании выражений RANGE или PARTITION BY, но я не могу понять, как указать оконный кадр, чтобы выбрать образцы, например. "на прошлой неделе".

SUM(ValueSum) OVER(PARTITION BY SensorId ORDER BY SampleTime ROWS 7 PRECEDING)/SUM(ValueCount) OVER(PARTITION BY SensorId ORDER BY SampleTime ROWS 7 PRECEDING) AS AverageLastWeek 

Я даже считал «Необычных Update», но помимо того, что грязно, я не думаю, что это имеет смысл с это много дней происходит усреднение.

+0

Вам нужна информация последнего месяца или lastmonth per Id? Я думаю о фильтрации SampleTime до последнего месяца. – MelgoV

+0

Если я правильно понимаю это, я считаю, что вы можете избежать использования функции PIVOT. Я не уверен, насколько быстрее это будет, но, безусловно, стоит того! – FutbolFan

+0

Мне нужны скользящие средние для каждого идентификатора и для каждого дня. Например, на сегодняшний день каждый Идентификатор будет приводить к появлению ряда с недельным средним значением по образцам от 4/30 до 5/6 и среднемесячного значения по образцам с 4/7 по 5/6. Таким образом, оконные рамы больше шагов или разделов. – hjort

ответ

0

Проверьте функции окна «Ввод» и «Задержка». Они были созданы именно для этой цели (выполнение agg-функций при перемещении окон по наборам результатов).

+0

Я действительно рассматривал аналитические функции LEAD/LAG в сочетании с PARTITION BY SensorId, но, насколько я мог судить, они основаны на подсчете строк и, таким образом, не обрабатывают дни без образцов - как и мой «ROWS 7 PRECEDING », о котором упоминалось в моем первоначальном вопросе. Можете ли вы дать мне конкретный пример, чтобы помочь мне лучше понять, как эти функции могут мне помочь? – hjort

+0

Это зависит от того, хотите ли вы, чтобы «отсутствующие» дни считались днями с 0 значением или вообще не учитывались. Для значения 0 вы можете создать таблицу «дней» и присоединиться к ней, таким образом, «заполнить» пробелы для смещения с 0. Для второго варианта вы можете снова создать таблицу «дней» и присоединиться к ней с помощью CTE ID и CTE.date между Days.date -X и Days.Date. Затем вы можете вычислить Count и Avg для каждой даты. –

0

Мой код скомпилирован быстрее, меньше сканирования и менее логических чтений с моим небольшим рандомизированным набором данных. С таким небольшим набором данных трудно сказать, и у меня нет ваших индексов и еще чего-то. Так что попробуйте это для себя. Во всяком случае, это проще, чем ваш запрос. Теперь, по моему запросу, средний месяц был сложным. Если вы хотите, вы можете сделать это как другие, которые используют определенное количество дней. Таким образом, вы можете сделать средний за предыдущие 30 дней. То, что я вложил, было средним только за текущий месяц. У меня есть чувство, что вы можете поместить свой подзапрос в этот, но я не пробовал этого, потому что здесь поздно.

Примечание: Предполагаемые строки предшествуют предположению, что они представляют собой строку для каждого дня без пробелов, иначе ваши данные будут искажены.

SELECT SensorID, 
     SampleDate, 
     AVG(avg_VALUE) OVER (PARTITION BY SensorID,SampleDate) avg_per_date, --but I only have one row per date so that's why its a whole number each time 
     AVG(avg_VALUE) OVER (PARTITION BY SensorID ORDER BY SampleDate ROWS BETWEEN 7 PRECEDING AND CURRENT ROW) AverageLastWeek, 
     AVG(avg_VALUE) OVER (PARTITION BY SensorID ORDER BY SampleDate ROWS BETWEEN 14 PRECEDING AND CURRENT ROW) AverageLast2Weeks, 
     AVG(avg_VALUE) OVER (PARTITION BY SensorID,MONTH(SampleDate) ORDER BY SampleDate ROWS UNBOUNDED PRECEDING) AverageCurrentMonth 
     --AVG(avg_VALUE) OVER (PARTITION BY SensorID ORDER BY SampleDate ROWS BETWEEN 30 PRECEDING AND CURRENT ROW) AverageLast30Days --alternatively you could do this 
FROM 
(
    SELECT SensorId,CAST(SampleTime AS DATE) SampleDate,AVG(Value) Avg_value 
    FROM Samples 
    WHERE Quality > .95 
    GROUP BY SensorId,CAST(SampleTime AS DATE) 
) S 
ORDER BY SensorID,SampleDate 

Если у вас есть вопросы или что-то еще, то дайте мне знать!

+0

Спасибо за конкретный пример.Тем не менее, это очень похоже на фрагмент кода, который я упомянул в нижней части моего исходного вопроса, за исключением того, что ваше предложение не делает правильное взвешенное среднее значение для правильной обработки дней с различным количеством выборок. И, как вы указываете сами, ни один из них не может обрабатывать дни без образцов, что является требованием. – hjort

+0

Да, это ограничения функций Windows. Единственный способ обойти это, что было бы не совсем красиво, - это создать список каждого из данных с минимальной даты до конца для каждого SensorID, затем LEFT JOIN его в вашу таблицу. Но в этот момент вы делаете так много дополнительной работы, вероятно, более эффективно придерживаться того, что у вас есть. – Stephan

0

Вы можете предоставить некоторые данные образца. это можно сделать с помощью рекурсивного CTE. Я могу помочь после того, как у меня есть некоторые тестовые данные для игры с

.
Смежные вопросы