如何根据开始日期和结束日期多次显示记录,包括日间隔

时间:2018-03-10 13:45:03

标签: sql sql-server sql-server-2008

这是我启发的问题:How to get all the monthly intervals between two dates?

QA在那里向Oracle提出问题,我喜欢这个问题,并希望在SQL Server 2008中这样做。

让我重新解释一下这个问题。假设我们有以下数据:

Event   StartDate    EndDate
A       2018-02-07   2018-04-22

然后预期的输出将是:

Event   Date
A       7 to 28
A       1 to 31
A       1 to 22

基本上,输出应显示EndDate和StartDate之间的日间隔。我用下面的查询处理了这个问题,效果很好,但我想知道是否有更好的方法来处理这个问题。任何建议将不胜感激!。

create table #temptable
([Event] varchar(5), dateinterval varchar(30))

declare @startDate date = '2018-02-07'
declare @endDate date = '2018-04-22'
declare @yeardiff int = (select DATEDIFF(month,@startDate,@endDate))

declare @counter int = 0
declare @tempDate date, @lastDateOfMonth date
declare @dateInterval varchar(100)

while @counter <= @yeardiff
begin
   set @tempDate = @startDate
   set @lastDateOfMonth = DATEADD(month, ((YEAR(@startDate) - 1900) * 12) + MONTH(@startDate), -1)

   if @counter = @yeardiff
   begin
      set @dateInterval = cast(DAY(@startDate) as varchar(2)) + ' to ' + cast(DAY(@endDate) as varchar(2))
   end
   else
   begin
      set @dateInterval = cast(DAY(@startDate) as varchar(2)) + ' to ' + cast(DAY(@lastDateOfMonth) as varchar(2))
   end

   insert into #temptable 
   values ('B',@dateInterval)

   set @startDate = DATEADD(m, DATEDIFF(m, -1, @startDate), 0)
   set @counter = @counter + 1
end

select * from #temptable

1 个答案:

答案 0 :(得分:3)

这在SQL Server中更容易,因为您可以使用递归CTE。 (实际上,在Oracle 12C中也有这些,所以同样的方法也可以。)

with CTE as (
      select event, startdate, enddate,
             dateadd(day, 1 - day(startdate), startdate) as month_start
      from t
      union all
      select event, startdate, enddate,
             dateadd(month, 1, month_start)
      from cte
      while month_start <= enddate
    )
select event, month_start,
       ((case when eomonth(enddate) = eomonth(month_start) -- last month
              then day(enddate)
              else day(eomonth(month_start))
         end) -
        (case when month_start < startdate  -- first month
              then day(startdate) - 1
              else 0
         end)
       ) as days_in_month
from cte;

这会逐个扩展每个事件的日期范围。然后计算当月的天数。

默认情况下,这最长可达100个月。如果您需要支持更长时间,可以使用maxrecursion选项。