我有一个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的新手,所以一个例子非常有帮助
答案 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