SQL - how do I generate rows for each month based on date ranges in existing dataset?

你离开我真会死。 提交于 2019-12-12 08:12:41

问题


assume I have a dataset:

rowID |  dateStart  |   dateEnd  | Year | Month
121   | 2013-10-03  | 2013-12-03 | NULL | NULL
143   | 2013-12-11  | 2014-03-11 | NULL | NULL
322   | 2014-01-02  | 2014-02-11 | NULL | NULL

And I want sql to generate the following datasource based on the dateStart and the dateEnd. Note the year and month grouping.

rowID |  dateStart  |   dateEnd  | Year | Month
121   | 2013-10-03  | 2013-12-03 | 2013 |   10
121   | 2013-10-03  | 2013-12-03 | 2013 |   11
121   | 2013-10-03  | 2013-12-03 | 2013 |   12
143   | 2013-12-11  | 2014-03-11 | 2013 |   12
143   | 2013-12-11  | 2014-03-11 | 2014 |    1
143   | 2013-12-11  | 2014-03-11 | 2014 |    2
143   | 2013-12-11  | 2014-03-11 | 2014 |    3
322   | 2014-01-02  | 2014-02-11 | 2014 |    1
322   | 2014-01-02  | 2014-02-11 | 2014 |    2

I'm having a hard time wrapping my head around this one. Any ideas?


回答1:


I find it easiest to approach these problems by creating a list of integers and then using that to increment the dates. Here is an example:

with nums as (
      select 0 as n
      union all
      select n + 1 as n
      from nums
      where n < 11
     )
select rowid, datestart, dateend,
       year(dateadd(month, n.n, datestart)) as yr,
       month(dateadd(month, n.n, datestart)) as mon
from table t join
     nums n
     on dateadd(month, n.n - 1, datestart) <= dateend;



回答2:


First, create a tabled-valued function that takes the 2 dates and returns the year and month as a table:

create function dbo.YearMonths(@StartDate DateTime, @EndDate DateTime)
returns @YearMonths table
([Year] int,
[Month] int)
as
begin

    set @EndDate = DATEADD(month, 1, @EndDate)
    while (@StartDate < @EndDate)
    begin

    insert into @YearMonths
    select YEAR(@StartDate), MONTH(@StartDate)  

    set @StartDate = DATEADD(month, 1, @StartDate)

    end

return
end

As an example the following:

select *
from dbo.YearMonths('1/1/2014', '5/1/2014')

returns:

Then you would join to it like this to get what you wanted:

select m.*, ym.Year, ym.Month
from myTable m
cross apply dbo.YearMonths(dateStart, dateEnd) ym



回答3:


Try this:

declare @months table(mth int)
insert into @months values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)

declare @calendar table(yr int,mth int)
insert into @calendar
select distinct year(datestart),mth
from tbl cross join @months
union
select distinct year(dateend),mth
from tbl cross join @months

select t.rowID, t.datestart, t.dateend, y.yr [Year], y.mth [Month] 
from
yourtable t
inner join @calendar y on year(datestart) = yr or year(dateend) = yr
where 
(mth >= month(datestart) and mth <= month(dateend) and year(datestart) = year(dateend))
or 
(year(datestart) < year(dateend)) 
 and 
 (year(datestart) = yr and mth >= month(datestart) --All months of start year
 or 
 (year(dateend) = yr and mth <= month(dateend))) -- All months of end year
order by t.rowID, [Year],[Month]

We create a 'Calendar table' which lists all the month and year combinations present in the source table. Then, we join the source table to the calendar table based on the year, and filter as required.



来源:https://stackoverflow.com/questions/24275945/sql-how-do-i-generate-rows-for-each-month-based-on-date-ranges-in-existing-dat

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!