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
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;
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)
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.
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
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.
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.