Минимальная и максимальная даты в непрерывном диапазоне дат, сгруппированные по имени - PullRequest
2 голосов
/ 07 мая 2020

У меня есть диапазоны данных с начальной и конечной датами для людей, я хочу получить непрерывные диапазоны дат только для людей:

Ввод:

NAME | STARTDATE      | END DATE
--------------------------------------
MIKE | **2019-05-15** | 2019-05-16 
MIKE | 2019-05-17     | **2019-05-18**
MIKE | 2020-05-18     | 2020-05-19

Ожидаемый результат, например:

MIKE | **2019-05-15** | **2019-05-18** 
MIKE | 2020-05-18     | 2020-05-19

Таким образом, в основном выводятся МИН и МАКС для каждого непрерывного периода для человека.

Благодарю за любую помощь.

Я пробовал следующий запрос:

With N AS (   SELECT Name, StartDate, EndDate
       , LastStop = MAX(EndDate) 
                    OVER (PARTITION BY Name ORDER BY StartDate, EndDate 
                          ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)   FROM   Table  ), B AS (   SELECT Name, StartDate, EndDate
       , Block = SUM(CASE WHEN LastStop Is Null Then 1
                          WHEN LastStop < StartDate Then 1
                          ELSE 0
                    END)
                 OVER (PARTITION BY Name ORDER BY StartDate, LastStop)   FROM   N ) SELECT Name
     , MIN(StartDate) DateFrom
     , MAX(EndDate) DateTo FROM   B GROUP BY Name, Block ORDER BY Name, Block

Но это без учета непрерывного периода. Показывает тот же ввод.

Ответы [ 3 ]

2 голосов
/ 07 мая 2020

Это тип проблемы с промежутками и островками. Нет необходимости расширять данные по дням! Это кажется очень неэффективным.

Вместо этого определите «острова». Здесь нет перекрытия - в вашем случае достаточно lag(). Затем кумулятивная сумма и агрегирование:

select name, min(startdate), max(enddate)
from (select t.*,
             sum(case when prev_enddate >= dateadd(day, -1, startdate) then 0 else 1 end) over 
                 (partition by name order by startdate) as grp
      from (select t.*,
                   lag(enddate) over (partition by name order by startdate) as prev_enddate
            from t
           ) t
     ) t
group by name, grp;

Здесь - скрипт db <>.

1 голос
/ 07 мая 2020

Вот пример использования специальной c таблицы подсчета

Пример или dbFiddle

;with cte as (
Select A.[Name]
      ,B.D
      ,Grp  = datediff(day,'1900-01-01',D) - dense_rank() over (partition by [Name] Order by D)
 From  YourTable A
 Cross Apply ( 
                Select Top (DateDiff(DAY,StartDate,EndDate)+1) D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),StartDate) 
                 From  master..spt_values n1,master..spt_values n2 
             ) B

)
Select [Name]
      ,StartDate= min(D)
      ,EndDate  = max(D)
 From  cte
 Group By [Name],Grp

Возвращает

Name    StartDate   EndDate
MIKE    2019-05-15  2019-05-18
MIKE    2020-05-18  2020-05-19

Просто для помощи с визуализацией CTE генерирует следующее

enter image description here

0 голосов
/ 07 мая 2020

Это даст вам тот же результат

    SELECT subquery.name,min(subquery.startdate),max(subquery.enddate1)
FROM (SELECT NAME,startdate,
      CASE WHEN EXISTS(SELECT yt1.startdate 
                       FROM t yt1 
                       WHERE yt1.startdate = DATEADD(day, 1, yt2.enddate) 
                       ) THEN null else yt2.enddate END as enddate1
      FROM t yt2) as subquery
GROUP by NAME, CAST(MONTH(subquery.startdate) AS VARCHAR(2)) + '-' + CAST(YEAR(subquery.startdate) AS VARCHAR(4))

Для CASE WHEN EXISTS я сослался на SQL CASE

Для группы по месяцам и годам вы можете увидеть это ГРУППА ПО МЕСЯЦУ И ГОДУ

DB_FIDDLE

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...