SQL日期范围分为几个月

时间:2016-03-02 17:55:10

标签: sql-server

我有一个sql表,其中包含以下内容

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

我需要像这样分解几个月......

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

我是SQL的新手,所以一个例子非常有帮助

3 个答案:

答案 0 :(得分:1)

日历

如果您有持久Calendar / DateRanges

,建议

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

实际上我在开始时并没有注意到这些时段可能在月份的第一天开始和结束,所以在完成整个脚本之后必须附加一些计算。后来我意识到<= >=日期过滤器产生了不必要的最后一行,它溢出了原始日期范围的上限。因此必须附加最终过滤器,并且在修改之后完全不喜欢这种方法))可能会应用一些增强功能但我不感兴趣。有很多方法可以完成这项任务。

有关给定时期的性质和目的的其他信息可能会改变不同方法的相关性和适用性

递归

不需要额外的数据,但如果日期范围足够宽,递归可能会很慢。

;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

答案 1 :(得分:0)

这段代码产生了几个月的愤怒,包括闰年,但我不能理解你的需求,所以解释得更好

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

答案 2 :(得分:0)

这适用于SQL Server 2012及更高版本;早期版本中不存在EOMONTH功能。

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