Here are couple of different ways to get you started. I have done only minimal testing with the sample data here, so use it as a starting point rather than a final solution.CREATE TABLE #tmp(ID INT, [Row] INT, [StartDate] DATETIME, endDate DATETIME);INSERT INTO #tmp VALUES(52,1,'10/1/2012','12/31/2012'),(52,2,'1/1/2013','12/31/2013'),(52,3,'1/1/2014','12/31/2014'),(52,4,'1/2/2015','12/31/2015'),(54,1,'10/1/2012','12/31/2012'),(54,2,'2/1/2013','12/31/2013');---ONE WAY;WITH cte AS( SELECT a.*, COALESCE(b.hasNext,0) hasNext, COALESCE(c.hasPrevious,0) AS hasPrevious FROM #tmp a OUTER APPLY ( SELECT CASE WHEN b.StartDate = DATEADD(dd,1,a.EndDate) THEN 1 ELSE 0 END AS hasNext FROM #tmp b WHERE b.id = a.id AND a.row+1 = b.row )b OUTER APPLY ( SELECT CASE WHEN a.StartDate = DATEADD(dd,1,b.EndDate) THEN 1 ELSE 0 END AS hasPrevious FROM #tmp b WHERE b.id = a.id AND a.row = b.row+1 ) c)SELECT a.Id, a.Row, a.StartDate, COALESCE(b.EndDate,a.EndDate) EndDateFROM cte a OUTER APPLY ( SELECT MIN(EndDate) EndDate FROM cte b WHERE b.id = a.id AND b.hasNext = 0 AND b.EndDate >= a.EndDate ) bWHERE a.hasPrevious = 0;--- ANOTHER WAY;WITH cte AS( SELECT *, endDate AS newEndDate, ROW_NUMBER() OVER (PARTITION BY id ORDER BY id,row) AS RN, 1 AS Lvl FROM #tmp a WHERE NOT EXISTS ( SELECT * FROM #tmp b WHERE b.id = a.ID AND a.StartDate = DATEADD(dd,1,b.EndDate)) UNION ALL SELECT t.*, t.EndDate AS NewEndDate, RN, lvl+1 FROM #tmp t INNER JOIN cte c ON c.id = t.id AND c.row +1 = t.row AND t.StartDate = DATEADD(dd,1,c.endDate)),cte2 AS (SELECT id,row,startdate, lvl, MAX(newEndDate) OVER (PARTITION BY Id, RN) AS NewEndDateFROM cte)SELECT id,row,startdate, newenddate FROM cte2 WHERE lvl = 1ORDER BY id, row;drop table #tmp;