SQL how to convert row with date range to many rows with each date

前端 未结 2 787
长发绾君心
长发绾君心 2020-12-16 01:58

If I have a table that looks like this

begin date      end date        data
 2013-01-01     2013-01-04       7
 2013-01-05     2013-01-06       9


        
相关标签:
2条回答
  • 2020-12-16 02:44

    You can use recursive CTE to get all the dates between two dates. Another CTE is to get ROW_NUMBERs to help you with those missing EndDates.

    DECLARE @startDate DATE
    DECLARE @endDate DATE
    
    SELECT @startDate = MIN(begindate) FROM Table1
    SELECT @endDate = MAX(enddate) FROM Table1
    
    ;WITH CTE_Dates AS 
    (
        SELECT @startDate AS DT
        UNION ALL
        SELECT DATEADD(DD, 1, DT)
        FROM CTE_Dates
        WHERE DATEADD(DD, 1, DT) <= @endDate
    )
    ,CTE_Data AS 
    (
        SELECT *, ROW_NUMBER() OVER (ORDER BY BeginDate) AS RN FROM Table1
    
    )
    SELECT DT, t1.data FROM CTE_Dates d
    LEFT JOIN CTE_Data t1 on d.DT 
    BETWEEN t1.[BeginDate] AND COALESCE(t1.EndDate, 
            (SELECT DATEADD(DD,-1,t2.BeginDate) FROM CTE_Data t2 WHERE t1.RN + 1 = t2.RN))
    

    SQLFiddle DEMO

    0 讨论(0)
  • 2020-12-16 02:49

    Using some sample data...

    create table data (begindate datetime, enddate datetime, data int);
    insert data select 
     '20130101', '20130104', 7 union all select
     '20130105', '20130106', 9;
    

    The Query: (Note: if you already have a numbers/tally table - use it)

    select dateadd(d,v.number,d.begindate) adate, data
      from data d
      join master..spt_values v on v.type='P'
           and v.number between 0 and datediff(d, begindate, enddate)
    order by adate;
    

    Results:

    |                       COLUMN_0 | DATA |
    -----------------------------------------
    | January, 01 2013 00:00:00+0000 |    7 |
    | January, 02 2013 00:00:00+0000 |    7 |
    | January, 03 2013 00:00:00+0000 |    7 |
    | January, 04 2013 00:00:00+0000 |    7 |
    | January, 05 2013 00:00:00+0000 |    9 |
    | January, 06 2013 00:00:00+0000 |    9 |
    

    Alternatively you can generate a number table on the fly (0-99) or as many numbers as you need

    ;WITH Numbers(number) AS (
      select top(100) row_number() over (order by (select 0))-1
      from sys.columns a
      cross join sys.columns b
      cross join sys.columns c
      cross join sys.columns d
      )
    select dateadd(d,v.number,d.begindate) adate, data
      from data d
      join Numbers v on v.number between 0 and datediff(d, begindate, enddate)
    order by adate;
    

    SQL Fiddle Demo

    0 讨论(0)
提交回复
热议问题