Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
761 views
in Technique[技术] by (71.8m points)

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

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

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

I want to split the multi day appointment as single days, so the result I am trying to achieve is like

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

So I have split the appointment id 10 into multiple rows. I checked a few other questions like here but those are to split just based on a single start date and end date and not based on table data

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...