Группировка по непрерывным датам, игнорирование выходных в SQL - PullRequest
2 голосов
/ 10 марта 2011

Я пытаюсь сгруппировать непрерывные диапазоны дат, чтобы показать минимальную и максимальную дату для каждого диапазона. До сих пор я использовал решение, подобное этому: http://www.sqlservercentral.com/articles/T-SQL/71550/, однако я использую SQL 2000, поэтому мне пришлось внести некоторые изменения. Это моя процедура до сих пор:

create table #tmp 
(
date smalldatetime,
rownum int identity
)

insert into #tmp
select distinct date from testDates order by date

select 
min(date) as dateRangeStart,
max(date) as dateRangeEnd, 
count(*) as dates, 
dateadd(dd,-1*rownum, date) as GroupID 
from #tmp
group by dateadd(dd,-1*rownum, date)

drop table #tmp

Это работает именно так, как я хочу, за исключением одного выпуска: выходные. Мои наборы данных не имеют записей для дат выходных, что означает, что любая найденная группа не превышает 5 дней. Например, в приведенных ниже результатах я хотел бы, чтобы последние 3 группы отображались как одна запись с dateRangeStart 10/6 и dateRangeEnd 10/20:

screenshot of results

Есть ли способ, которым я могу настроить это, чтобы игнорировать перерыв в диапазоне дат, если этот перерыв - просто выходные?

Спасибо за помощь.

1 Ответ

2 голосов
/ 11 марта 2011

EDITED

Мне не очень понравилась моя предыдущая идея. Вот лучший, я думаю:

  1. На основе первой и последней дат из набора групп, которые необходимо сгруппировать, подготовьте список всех промежуточных дат выходных.
  2. Вставьте рабочие даты вместе с упорядоченными датами выходных, чтобы им всем было присвоено rownum значений в соответствии с их обычным порядком.
  3. Используйте ваш метод поиска смежных диапазонов со следующими модификациями:

    1) при расчете dateRangeStart, если это выходной день, выберите ближайший следующий день недели;

    2) соответственно для dateRangeEnd, если это выходной день, выберите ближайший предыдущий день недели;

    3) при подсчете дат для группы выбирайте только рабочие дни.

  4. Выберите из результирующего набора только те строки, где dates > 0, тем самым исключая группы, образованные только по выходным.

А вот реализация метода, где предполагается, что неделя начинается в воскресенье (DATEPART возвращает 1), а выходные дни - воскресенье и суббота:

DECLARE @tmp TABLE (date smalldatetime, rownum int IDENTITY);
DECLARE @weekends TABLE (date smalldatetime);
DECLARE @minDate smalldatetime, @maxDate smalldatetime, @date smalldatetime;
/* #1 */
SELECT @minDate = MIN(date), @maxDate = MAX(date)
FROM testDates;
SET @date = @minDate - DATEPART(dw, @minDate) + 7;
WHILE @date < @maxDate BEGIN
  INSERT INTO @weekends
  SELECT @date UNION ALL
  SELECT @date + 1;
  SET @date = @date + 7;
END;
/* #2 */
INSERT INTO @tmp
SELECT date FROM testDates
UNION
SELECT date FROM @weekends
ORDER BY date;
/* #3 & #4 */
SELECT *
FROM (
  SELECT
    MIN(date + CASE DATEPART(dw, date) WHEN 1 THEN 1 WHEN 7 THEN 2 ELSE 0 END)
      AS dateRangeStart,
    MAX(date - CASE DATEPART(dw, date) WHEN 1 THEN 2 WHEN 7 THEN 1 ELSE 0 END)
      AS dateRangeEnd,
    COUNT(CASE WHEN DATEPART(dw, date) NOT IN (1, 7) THEN date END) AS dates,
    DATEADD(d, -rownum, date) AS GroupID
  FROM @tmp
  GROUP BY DATEADD(d, -rownum, date)
) s
WHERE dates > 0;
...