Je souhaite remplir l'objet de calendrier qui requiert des informations sur les dates de début et de fin. J'ai une colonne qui contient une séquence de dates. Certaines dates sont consécutives (il y a une différence d'un jour) et d'autres pas.
InfoDate
2013-12-04 consecutive date [StartDate]
2013-12-05 consecutive date
2013-12-06 consecutive date [EndDate]
2013-12-09 [startDate]
2013-12-10 [EndDate]
2014-01-01 [startDate]
2014-01-02
2014-01-03 [EndDate]
2014-01-06 [startDate]
2014-01-07 [EndDate]
2014-01-29 [startDate]
2014-01-30
2014-01-31 [EndDate]
2014-02-03 [startDate]
2014-02-04 [EndDate]
Je veux choisir les dates de début et de fin de chaque plage de dates consécutives (la première et la dernière du bloc).
StartDate EndDate
2013-12-04 2013-12-06
2013-12-09 2013-12-10
2014-01-01 2014-01-03
2014-01-06 2014-01-07
2014-01-29 2014-01-31
2014-02-03 2014-02-04
Je veux résoudre le problème en utilisant uniquement SQL.
Aucune jointure ni CTE récursif requis. La solution standard entre îlots et îlots consiste à grouper par (valeur moins ligne_numéro), car elle est invariante dans une séquence consécutive. Les dates de début et de fin ne sont que les valeurs MIN () et MAX () du groupe.
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)
Voici..
;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
s'il vous plaît n'oubliez pas de le marquer comme réponse, si cela répond à votre question.
Voici mon échantillon avec les données de test:
--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
Remplacez les données #tmp par votre table actuelle.
J'ai inséré ces valeurs dans une table appelée #consec
, puis les résultats suivants:
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
Tant que vos dates ne se chevauchent pas, cela devrait faire le travail pour vous.