#sql #sql-server #aggregate #datediff #sliding-window
#sql #sql-сервер #агрегировать #datediff #скользящее окно
Вопрос:
[Запрос и вопрос отредактированы и исправлены благодаря комментариям @Gordon Linoff и @shawnt00]
Недавно я унаследовал SQL-запрос, который вычисляет количество некоторых событий во временных окнах продолжительностью 30 дней из базы данных журналов. Он использует CTE (общее табличное выражение) для генерации 30-дневных диапазонов с момента «2019-01-01» по настоящее время. И затем он подсчитывает обращения в эти интервалы 30/60/90 дней. Я не уверен, что это лучший метод. Все, что я знаю, это то, что для запуска требуется много времени, и я не понимаю на 100%, как именно это работает. Поэтому я пытаюсь перестроить его эффективным способом (возможно, как сейчас, это самый эффективный способ, я не знаю).
У меня есть несколько вопросов:
- Одна из вещей, которые я замечаю, заключается в том, что вместо использования DATEDIFF запрос просто вычитает количество дней из даты.Это вообще хорошая практика?
- Есть ли лучший способ сравнения времени?
- Есть ли лучший способ сделать все это? Суть в том, что мне нужно агрегировать данные по количеству вхождений за периоды времени в 30, 60 и 90 дней.
Примечание: исходный формат LogDate похож на 2019-04-01 18:30:12.000.
DECLARE @dt1 Datetime='2019-01-01'
DECLARE @dt2 Datetime=getDate();
WITH ctedaterange
AS (SELECT [Dates]=@dt1
UNION ALL
SELECT [dates] 30
FROM ctedaterange
WHERE [dates] 30<= @dt2)
SELECT
[dates],
lt.Activity, COUNT(*) as Total,
SUM(CASE WHEN lt.LogDate <= dates and lt.LogDate > dates - 90 THEN 1 ELSE 0 END) AS Activity90days,
SUM(CASE WHEN lt.LogDate <= dates and lt.LogDate > dates - 60 THEN 1 ELSE 0 END) AS Activity60days,
SUM(CASE WHEN lt.LogDate <= dates and lt.LogDate > dates - 30 THEN 1 ELSE 0 END) AS Activity30days
FROM ctedaterange AS cte
JOIN (SELECT Activity, CONVERT(DATE, LogDate) as LogDate FROM LogTable) AS lt
ON cte.[dates] = lt.LogDate
group by [dates], lt.Activity
OPTION (maxrecursion 0)
Образец набора данных (таблица журналов):
LogDate, Activity
2020-02-25 01:10:10.000, Activity01
2020-04-14 01:12:10.000, Activity02
2020-08-18 02:03:53.000, Activity02
2019-10-29 12:25:55.000, Activity01
2019-12-24 18:11:11.000, Activity03
2019-04-02 03:33:09.000, Activity01
Ожидаемый результат (результат не отражает данные, указанные выше, поскольку мне потребуется слишком много строк в наборе примеров, которые будут показаны в этом сообщении)
Как я уже говорил выше, суть в следующем: мне нужно агрегировать данные по количеству вхождений в периоды времени 30, 60 и 90 дней.
Activity, Activity90days, Activity60days, Activity30days
Activity01, 3, 0, 1
Activity02, 1, 10, 2
Activity03, 5, 1, 3
Спасибо за любое предложение.
Комментарии:
1. (1) Отметьте используемую базу данных. (2) Предоставьте образцы данных и желаемые результаты. (3) В вашем вопросе говорится об одном таймфрейме, но вопрос содержит другие таймфреймы.
2. Это создает список всех дат, а затем подсчитывает количество событий журнала на каждую дату. Он обрабатывает те даты, когда события не были записаны. По-видимому, между двумя таблицами отсутствует соединение, что, вероятно, объясняет, почему оно медленное (и я сомневаюсь, что цифры также верны.)
3. Кроме того, вы группируете по
lt.Activity
, но возвращаетеlt.Event
неагрегированный. Это должно вызвать ошибку.4. Извините, Гордон и Шон, я вставил из разных запросов. Я исправил временные рамки. Я добавлю некоторые примеры данных.
5. Привет @shawnt00, какое соединение будет отсутствовать?
Ответ №1:
В SQL Server пока нет возможности изменять диапазон значений рамки окна аналитической функции. Поскольку вы сгенерировали все возможные даты и у вас уже есть подсчеты по дате, очень легко просмотреть определенное количество (агрегированных) строк, чтобы получить правильные итоги. Вот мое предложенное выражение на 90 дней:
sum(count(LogDate)) over (
partition by Activity order by [dates]
with rows between 89 preceding and current row
)