SQL Server contiguous dates - summarizing multiple rows into contiguous start and end date rows without CTE's, loops,…s

≡放荡痞女 提交于 2021-02-08 10:31:54

问题


Is it possible to write an sql query that will summarize rows with start and end dates into rows that have contiguous start and end dates?

The constraint is that it has to be regular sql, i.e. no CTE's, loops and the like as a third party tool is used that only allows an sql statement to start with Select.

e.g.:

ID       StartDate         EndDate

1001,      Jan-1-2018,       Jan-04-2018
1002,      Jan-5-2018,       Jan-13-2018
1003,      Jan-14-2018,      Jan-18-2018 
1004,      Jan-25-2018,      Feb-05-2018 

The required output needs to be:

Jan-1-2018,      Jan-18-2018 
Jan-25-2018,     Feb-05-2018 

Thank you


回答1:


You can take advantage of both window functions and the use of a concept called gaps-and-islands. In your case, contiguous dates would be the island, and the the gaps are self explanatory.

I wrote the answer below in a verbose way to help make it clear what the query is doing, but it could most likely be written in a different way that is more concise. Please see my comments in the answer explaining what each step (sub-query) does.

--Determine Final output
select min(c.StartDate) as StartDate
, max(c.EndDate) as EndDate
from (
    --Assign a number to each group of Contiguous Records
    select b.ID
    , b.StartDate
    , b.EndDate
    , b.EndDatePrev
    , b.IslandBegin
    , sum(b.IslandBegin) over (order by b.ID asc) as IslandNbr
    from (
        --Determine if its Contiguous (IslandBegin = 1, means its not Contiguous with previous record)
        select a.ID
        , a.StartDate
        , a.EndDate
        , a.EndDatePrev
        , case when a.EndDatePrev is NULL then 1
               when datediff(d, a.EndDatePrev, a.StartDate) > 1 then 1
               else 0
          end as IslandBegin
        from (
            --Determine Prev End Date
            select tt.ID
            , tt.StartDate
            , tt.EndDate
            , lag(tt.EndDate, 1, NULL) over (order by tt.ID asc) as EndDatePrev
            from dbo.Table_Name as tt
            ) as a
        ) as b
    ) as c
group by c.IslandNbr
order by c.IslandNbr



回答2:


I hope following SQL query can help you to identify gaps and covered dates for given case

I did not use a CTE expression of a dates table function, etc On the other hand, I used a numbers table using master..spt_values to generate the dates table as the main table of a LEFT join You can create a numbers table or a dates table if it does not fit to your requirements

In the query, to catch changes between borders I used SQL LAG() function which enables me to compare with previous value of a column in a sorted list

select
    max(startdate) as startdate,
    max(enddate) as enddate
from (
    select 
        date,
        case when exist = 1 then date else null end as startdate,
        case when exist = 0 then dateadd(d,-1,date) else null end as enddate,
        ( row_number() over (order by date) + 1) / 2 as rn
    from (
        select date, exist, case when exist <> (lag(exist,1,'') over (order by date)) then 1 else 0 end as changed
        from (
            select 
                d.date,
                case when exists (select * from Periods where d.date between startdate and enddate) then 1 else 0 end as exist
            from (
                SELECT dateadd(dd,number,'20180101') date
                FROM master..spt_values
                 WHERE Type = 'P' and dateadd(dd,number,'20180101') <= '20180228'
            ) d 

        ) cte   
    ) tbl
    where changed = 1
) dates
group by rn

Here is the result



来源:https://stackoverflow.com/questions/47934930/sql-server-contiguous-dates-summarizing-multiple-rows-into-contiguous-start-an

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!