#sql-server #group-by #window-functions
#sql-server #группируйте по #окно-функции
Вопрос:
Я использую SQL Server 2017. У меня есть таблица запросов, чтобы упростить задачу, есть только один столбец RequestDate . Например,
RquestDate
4/11
4/12
4/13
4/16
4/18
Мне нужно сгруппировать по дате запроса, учитывая дни ожидания. Если день ожидания равен 0, результат должен быть таким же, как в необработанной таблице.
Если день ожидания равен 1, это означает, что когда я смотрю на 4/11, мне нужно проверить, существует ли 4/12, если да, сгруппируйте 4/12 в 4/11. Результат:
4/11 --it groups 4/12
4/13
4/16
4/18
Если день ожидания равен 2, при просмотре 4/11 он группирует в него 4/12, 4/13.
Результат:
4/11 -- group 4/12 and 4/13.
4/16 -- group 4/18
Таким образом, эта проблема отличается от типичной проблемы с пробелами и островами. Потому что, когда группируются даты, там может быть пробел, например, когда прогнозируемый день равен 2, 4/16, группам 4/17 и 4/18.
Я пробовал несколько способов, но не могу найти достойного решения.
Комментарии:
1. Пожалуйста, покажите нам, что вы пробовали. И уточните, фиксировано ли количество дней ожидания для данного выполнения запроса? И я рекомендую вам добавить некоторые образцы данных, используя DDL DML, поскольку это облегчает людям возможность играть и, возможно, отвечать.
2. И хотя 13 всего на 1 больше, чем 12, он определенно запускает новую группу и не группируется с 11 и 12?
Ответ №1:
Может работать рекурсивное общее табличное выражение.
- Выберите дату начала запроса с помощью
min()
функции. - Используйте ту же дату, что и дата начала группировки.
Шаги 1 и 2 составляют строку привязки / начала рекурсии.
- Рекурсивно перейдите к поиску даты следующего запроса. Эта дата выше, чем предыдущая дата (
r.RequestDate > c.RequestDate
), и перед ней нет другой строки, которая соответствует тем же критериям (not exists ... r2.RequestDate < r.RequestDate
). - Если текущая дата запроса (с шага 3) попадает в интервал ожидания, то сохраните дату начала группировки (
then c.RequestGroupDate
), в противном случае запустите новую группу в текущую дату запроса (else r.RequestDate
).
Шаги 3 и 4 составляют рекурсивную часть CTE.
- После рекурсии каждая дата запроса в качестве соответствующей даты группировки запроса.
group by r.RequestGroupDate
Предложение уменьшает вывод результата до отдельных значений.
Примерные данные
create table Requests
(
RequestDate date
);
insert into Requests (RequestDate) values
('2021-04-11'),
('2021-04-12'),
('2021-04-13'),
('2021-04-16'),
('2021-04-18');
Решение
declare @lookAhead int = 1; -- look ahead days parameter
with rcte as
(
select min(r.RequestDate) as RequestDate,
min(r.RequestDate) as RequestGroupDate
from Requests r
union all
select r.RequestDate,
case
when datediff(day, c.RequestGroupDate, r.RequestDate) <= @lookAhead
then c.RequestGroupDate
else r.RequestDate
end
from rcte c
join Requests r
on r.RequestDate > c.RequestDate
where not exists ( select 'x'
from Requests r2
where r2.RequestDate > c.RequestDate
and r2.RequestDate < r.RequestDate )
)
select r.RequestGroupDate
from rcte r
group by r.RequestGroupDate;
Результат
Для @lookAhead = 1
:
RequestGroupDate
----------------
2021-04-11
2021-04-13
2021-04-16
2021-04-18
Для @lookahead = 2
:
RequestGroupDate
----------------
2021-04-11
2021-04-16
Поиграйте, чтобы увидеть вещи в действии.
Комментарии:
1. Не могли бы вы задокументировать логику того, как это работает. Это отличное решение, но сложно понять, как оно работает.
2. Если я правильно понимаю, предложение «где не существует» заставляет CTE обрабатывать таблицу строка за строкой. Это похоже на перемещение курсора от самой старой даты запроса к самой новой дате запроса. Для каждого перемещения он переносит предыдущий RequestGroupDate и определяет, принадлежит ли текущая строка предыдущему ReuqestGroupDate. Если нет, он использует свой собственный RequestDate в качестве нового RequestGroupDate. Меня беспокоит только то, что максимальная глубина рекурсивного SQL CTE равна 32767. Означает ли это, что он не может обрабатывать таблицу с более чем 32767 строками. Я протестирую это. @Sander.
3. @DaleK, я добавил описание решения с пошаговыми инструкциями.
4. @FrankZhang, в случае, если ограничение рекурсии может создать проблему, пожалуйста, задайте другой вопрос и объясните, почему это решение не работает для вас.
5. @Sander спасибо за это — все еще пытаюсь разобраться в шаге 3 🙂 это требует некоторого сгибания ума.