Break into multiple rows based on date range of a single row

前端 未结 2 1696
盖世英雄少女心
盖世英雄少女心 2020-12-20 07:09

I have a table which captures appointments, some are single day appointments and some are multi day appointments, so the data looks like

AppointmentId   Star         


        
相关标签:
2条回答
  • 2020-12-20 07:21

    Clearly a Calendar/Tally table would be the way to go as SqlZim illustrated (+1), however you can use an ad-hoc tally table with a CROSS APPLY.

    Example

    Select A.AppointmentId   
          ,StartDate = B.D  
          ,EndDate   = B.D
     From  YourTable A
     Cross Apply (
                    Select Top (DateDiff(DD,A.StartDate,A.EndDate)+1) D=DateAdd(DD,-1+Row_Number() Over (Order By Number),A.StartDate) 
                     From  master..spt_values
                 ) B
    

    Returns

    AppointmentId   StartDate   EndDate
    9               2017-04-12  2017-04-12
    10              2017-05-01  2017-05-01
    10              2017-05-02  2017-05-02
    10              2017-05-03  2017-05-03
    11              2017-06-01  2017-06-01
    
    0 讨论(0)
  • 2020-12-20 07:36

    You can use a Calendar or dates table for this sort of thing.

    For only 152kb in memory, you can have 30 years of dates in a table with this:

    /* dates table */
    declare @fromdate date = '20000101';
    declare @years    int  = 30;
    /* 30 years, 19 used data pages ~152kb in memory, ~264kb on disk */
    ;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
    select top (datediff(day, @fromdate,dateadd(year,@years,@fromdate)))
        [Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,@fromdate))
    into dbo.Dates
    from n as deka cross join n as hecto cross join n as kilo
                   cross join n as tenK cross join n as hundredK
    order by [Date];
    create unique clustered index ix_dbo_Dates_date
      on dbo.Dates([Date]);
    

    Without taking the actual step of creating a table, you can use it inside a common table expression with just this:

    declare @fromdate date = '20161229'; 
    declare @thrudate date = '20170103';
    ;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
    , dates as (
      select top (datediff(day, @fromdate, @thrudate)+1) 
          [Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,@fromdate))
      from n as deka cross join n as hecto cross join n as kilo
                    cross join n as tenK cross join n as hundredK
       order by [Date]
    )
    select [Date]
    from dates;
    

    Use either like so:

    select 
        t.AppointmentId
      , StartDate = d.date
      , EndDate = d.date
    from dates d
      inner join appointments t
        on d.date >= t.StartDate
       and d.date <= t.EndDate
    

    rextester demo: http://rextester.com/TNWQ64342

    returns:

    +---------------+------------+------------+
    | AppointmentId | StartDate  |  EndDate   |
    +---------------+------------+------------+
    |             9 | 2017-04-12 | 2017-04-12 |
    |            10 | 2017-05-01 | 2017-05-01 |
    |            10 | 2017-05-02 | 2017-05-02 |
    |            10 | 2017-05-03 | 2017-05-03 |
    |            11 | 2017-06-01 | 2017-06-01 |
    +---------------+------------+------------+
    

    Number and Calendar table reference:

    • Generate a set or sequence without loops - 1 - Aaron Bertrand
    • Generate a set or sequence without loops - 2 - Aaron Bertrand
    • Generate a set or sequence without loops - 3 - Aaron Bertrand
    • The "Numbers" or "Tally" Table: What it is and how it replaces a loop - Jeff Moden
    • Creating a Date Table/Dimension in sql Server 2008 - David Stein
    • Calendar Tables - Why You Need One - David Stein
    • Creating a date dimension or calendar table in sql Server - Aaron Bertrand
    • tsql Function to Determine Holidays in sql Server - Aaron Bertrand
    • F_table_date - Michael Valentine Jones
    0 讨论(0)
提交回复
热议问题