La plage de dates SQL se déchire en mois

J'ai une table SQL qui a les éléments suivants

ID StartDate EndDate 10 2015-12-01 2016-05-31 15 2016-01-05 2016-07-04 20 2016-02-10 2016-08-09 

J'ai besoin de décomposer les mois comme ça …

 ID StartDate EndDate 10 2015-12-01 2015-12-31 10 2016-01-01 2016-01-31 10 2016-02-01 2016-02-29 10 2016-03-01 2016-03-31 10 2016-04-01 2016-04-30 10 2016-05-01 2016-05-31 15 2016-01-05 2016-02-04 15 2016-02-05 2016-03-04 15 2016-03-05 2016-04-04 15 2016-04-05 2016-05-04 15 2016-05-05 2016-06-04 15 2016-06-05 2016-07-04 etc 

Je suis nouveau à SQL donc un exemple serait très utile

Calendrier

recommandé si vous avez une table Calendar / DateRanges persistante

 declare @datebegin date = '20140101' ;with cteCalendar as ( select c.period_start, dateadd(dd, -1, dateadd(mm, 1, c.period_start)) as period_end from ( select top 100 dateadd(mm, row_number() over(order by sc.object_id)-1, @datebegin) as period_start from sys.columns sc order by period_start ) c ), cteData as ( select cast(10 as int) as id, cast('20151201' as date) as StartDate, cast('20160531' as date) as EndDate union all select 15, '20160105', '20160704' union all select 25, '20160210', '20160809' ), cteDataEx as ( select d.id, d.StartDate, d.EndDate, datepart(dd, d.StartDate)-1 as DateOffset from cteData d ) select d.id, dateadd(dd, d.DateOffset, c.period_start) as StartDate, dateadd(dd, d.DateOffset, c.period_end) as EndDate from cteDataEx d inner join cteCalendar c on c.period_start <= d.EndDate and c.period_end >= d.StartDate where dateadd(dd, d.DateOffset, c.period_end) <= d.EndDate order by id, StartDate 

En fait, je n'ai pas remarqué au début que les périodes peuvent commencer et se terminer pas au 1er jour du mois, donc j'ai dû append quelques calculs après l'achèvement de l'set du script. Plus tard j'ai réalisé que <= >= filter de date produit la dernière rangée inutile qui dépasse la limite haute de la plage de dates originale. Donc a dû append le filter final et après cette modification n'aime pas cette approche totalement)) Peut être quelques améliorations peuvent être appliquées, mais je ne suis pas intéressé par. Beaucoup de façons d'accomplir cette tâche.

Des informations supplémentaires sur la nature et le but des périodes données peuvent altérer la pertinence et l'applicabilité des différentes approches

Récursivité

aucune donnée supplémentaire requirejse mais la récursivité peut être lente si les plages de dates peuvent être assez larges.

 ;with cteData as ( select cast(10 as int) as id, cast('20151201' as date) as StartDate, cast('20160531' as date) as EndDate union all select 15, '20160105', '20160704' union all select 25, '20160210', '20160809' ), ctePeriods as ( select d.id, d.StartDate, dateadd(dd, -1, dateadd(mm, 1, d.StartDate)) as EndDate, d.EndDate as _EndDate from cteData d union all select p.id, dateadd(mm, 1, p.StartDate), dateadd(dd, -1, dateadd(mm, 2, p.StartDate)), p._EndDate from ctePeriods p where p.EndDate < p._EndDate ) select p.id, p.StartDate, p.EndDate from ctePeriods p order by id, startdate 

ce code génère la rage des mois, y compris l'année bissextile, mais je ne comprends pas votre besoin alors mieux expliquer

 create table #dia_meses (mes int, messtr varchar(2), dia_final varchar(2)) insert into #dia_meses values(1,'01','31') insert into #dia_meses values(2,'02','29') insert into #dia_meses values(3,'03','31') insert into #dia_meses values(4,'04','30') insert into #dia_meses values(5,'05','31') insert into #dia_meses values(6,'06','30') insert into #dia_meses values(7,'07','31') insert into #dia_meses values(8,'08','31') insert into #dia_meses values(9,'09','30') insert into #dia_meses values(10,'10','31') insert into #dia_meses values(11,'11','30') insert into #dia_meses values(12,'12','31') declare @year varchar(4) declare @contador int set @year =convert(varchar,DATEPART(YEAR,GETDATE())) set @contador =convert(varchar,DATEPART(month,GETDATE())) declare @dataIni datetime declare @datafim datetime set @dataIni=(select @year+'-'+messtr+'-01' from #dia_meses where mes=@contador) --pulo do gato ano bissexto if(@contador=2) begin if(select ISDATE(@year+'-'+messtr+'-'+dia_final) from #dia_meses where mes=@contador)=0 begin set @datafim=(select @year+'-'+messtr+'-28' from #dia_meses where mes=@contador) end else--ano bissexto begin set @datafim=(select @year+'-'+messtr+'-'+dia_final from #dia_meses where mes=@contador) end end else begin set @datafim=(select @year+'-'+messtr+'-'+dia_final from #dia_meses where mes=@contador) end print @dataIni print @dataFim 

Cela fonctionnera sur SQL Server 2012 et plus; la fonction EOMONTH n'existe pas sur les versions antérieures.

 DECLARE @table TABLE (ID INT, StartDate DATE, EndDate DATE) DECLARE @outtable TABLE (ID INT, StartDate DATE, EndDate DATE) DECLARE @ID INT DECLARE @StartDate DATE DECLARE @Date1 DATE DECLARE @Date2 DATE DECLARE @EndDate DATE INSERT INTO @table VALUES (10,'2015-12-01','2016-05-31') ,(15,'2016-01-05','2016-07-04') ,(20,'2016-02-10','2016-08-09') DECLARE tablecursor CURSOR FOR SELECT * FROM @table OPEN tablecursor FETCH NEXT FROM tablecursor INTO @ID, @StartDate, @EndDate WHILE @@FETCH_STATUS = 0 BEGIN SET @Date1 = @StartDate SET @Date2 = EOMONTH(@Date1) WHILE @Date1 < @EndDate BEGIN PRINT CONVERT(VARCHAR,@ID) + ' ' + CONVERT(VARCHAR,@Date1) + ' ' + CONVERT(VARCHAR,@Date2) INSERT INTO @outtable SELECT @ID, @Date1, @Date2 SET @Date1 = DATEADD(DAY,1,@Date2) SET @Date2 = EOMONTH(@Date1) IF @Date2 > @EndDate BEGIN SET @Date2 = @EndDate END END FETCH NEXT FROM tablecursor INTO @ID, @StartDate, @EndDate END SELECT * FROM @outtable CLOSE tablecursor DEALLOCATE tablecursor