Detect consecutive dates ranges using SQL

后端 未结 7 1190
南笙
南笙 2020-11-28 07:54

I want to fill the calendar object which requires start and end date information. I have one column which contains a sequence of dates. Some of the dates are consecutive (ha

相关标签:
7条回答
  • 2020-11-28 08:28
    SELECT InfoDate ,
        CASE
          WHEN TRUNC(InfoDate - 1) = TRUNC(lag(InfoDate,1,InfoDate) over (order by InfoDate))
          THEN NULL
          ELSE InfoDate
        END STARTDATE,
        CASE
          WHEN TRUNC(InfoDate + 1) = TRUNC(lead(InfoDate,1,InfoDate) over (order by InfoDate))
          THEN NULL
          ELSE InfoDate
        END ENDDATE
      FROM TABLE;
    
    0 讨论(0)
  • 2020-11-28 08:29

    No joins or recursive CTEs needed. The standard gaps-and-island solution is to group by (value minus row_number), since that is invariant within a consecutive sequence. The start and end dates are just the MIN() and MAX() of the group.

    WITH t AS (
      SELECT InfoDate d,ROW_NUMBER() OVER(ORDER BY InfoDate) i
      FROM @d
      GROUP BY InfoDate
    )
    SELECT MIN(d),MAX(d)
    FROM t
    GROUP BY DATEDIFF(day,i,d)
    
    0 讨论(0)
  • 2020-11-28 08:36

    I have inserted these values into a table called #consec and then perforemed the following:

    select t1.*
    ,t2.infodate as binfod
    into #temp1
    from #consec t1
    left join #consec t2 on dateadd(DAY,1,t1.infodate)=t2.infodate
    
    select t1.*
    ,t2.infodate as binfod
    into #temp2
    from #consec t1
    left join #consec t2 on dateadd(DAY,1,t2.infodate)=t1.infodate
    ;with cte as(
    select infodate,  ROW_NUMBER() over(order by infodate asc) as seq from #temp1
    where binfod is null
    ),
    cte2 as(
    select infodate, ROW_NUMBER() over(order by infodate asc) as seq from #temp2
    where binfod is null
    )
    
    select t2.infodate as [start_date]
    ,t1.infodate as [end_date] from cte t1
    left join cte2 t2 on t1.seq=t2.seq 
    

    As long as your date periods are not overlapping, that should do the job for you.

    0 讨论(0)
  • 2020-11-28 08:36

    You can do like this and here is the sqlfiddle

    select
      min(ndate) as start_date,
      max(ndate) as end_date
    from
    (select
      ndate,
      dateadd(day, -row_number() over (order by ndate), ndate) as rnk
     from dates
     ) t
     group by
       rnk
    
    0 讨论(0)
  • 2020-11-28 08:38

    Here you go..

    ;WITH CTEDATES
    AS
    (
        SELECT ROW_NUMBER() OVER (ORDER BY Infodate asc ) AS ROWNUMBER,infodate FROM YourTableName  
    
    ),
     CTEDATES1
    AS
    (
       SELECT ROWNUMBER, infodate, 1 as groupid FROM CTEDATES WHERE ROWNUMBER=1
       UNION ALL
       SELECT a.ROWNUMBER, a.infodate,case datediff(d, b.infodate,a.infodate) when 1 then b.groupid else b.groupid+1 end as gap FROM CTEDATES A INNER JOIN CTEDATES1 B ON A.ROWNUMBER-1 = B.ROWNUMBER
    )
    
    select min(mydate) as startdate, max(infodate) as enddate from CTEDATES1 group by groupid
    

    please don't forget to mark it as answer, if this answers your question.

    0 讨论(0)
  • 2020-11-28 08:46

    Here it is my sample with test data:

    --required output
    -- 01 - 03
    -- 08 - 09
    -- 12 - 14
    
    DECLARE @maxRN int;
    WITH #tmp AS (
                    SELECT CAST('2013-01-01' AS date) DT
        UNION ALL   SELECT CAST('2013-01-02' AS date)
        UNION ALL   SELECT CAST('2013-01-03' AS date)
        UNION ALL   SELECT CAST('2013-01-05' AS date)
        UNION ALL   SELECT CAST('2013-01-08' AS date)
        UNION ALL   SELECT CAST('2013-01-09' AS date)
        UNION ALL   SELECT CAST('2013-01-12' AS date)
        UNION ALL   SELECT CAST('2013-01-13' AS date)
        UNION ALL   SELECT CAST('2013-01-14' AS date)
    ),
    #numbered AS (
        SELECT 0 RN, CAST('1900-01-01' AS date) DT
        UNION ALL
        SELECT ROW_NUMBER() OVER (ORDER BY DT) RN, DT
        FROM #tmp
    )
    
    SELECT * INTO #tmpTable FROM #numbered;
    SELECT @maxRN = MAX(RN) FROM #tmpTable;
    
    INSERT INTO #tmpTable
    SELECT @maxRN + 1, CAST('2100-01-01' AS date);
    
    WITH #paired AS (
        SELECT 
        ROW_NUMBER() OVER(ORDER BY TStart.DT) RN, TStart.DT DTS, TEnd.DT DTE
        FROM #tmpTable TStart
        INNER JOIN #tmpTable TEnd 
        ON TStart.RN = TEnd.RN - 1
        AND DATEDIFF(dd,TStart.DT,TEnd.DT) > 1  
    )
    
    SELECT TS.DTE, TE.DTs 
    FROM #paired TS
    INNER JOIN #paired TE ON TS.RN = TE.RN -1
    AND TS.DTE <> TE.DTs -- you could remove this filter if you want to have start and end on the same date
    
    DROP TABLE #tmpTable
    

    Replace #tmp data with your actual table.

    0 讨论(0)
提交回复
热议问题