使用单列日期分隔记录以共享日期范围

时间:2017-03-17 17:19:50

标签: sql sql-server-2008 tsql common-table-expression recursive-query

我的表中有以下记录,名为Disbursements。我喜欢做的是根据开始和结束服务日期将每条记录分成单独的记录记录集,只使用一列日期。

RUN /etc/init.d/postgresql start &&\
    psql --command "CREATE USER matt WITH PASSWORD 'test123';" &&\
    createdb test_db &&\
    psql --command "GRANT ALL PRIVILEGES ON DATABASE test_db TO matt;"

我的目标结果看起来像

 DisbursementID ServiceProviderID Original CircuitID   Beginning_Service_Date Ending_Service_Date Amount
 -------------- ----------------- -------- ----------- ---------------------- ------------------- -----------
 53562          673               0        1814        2015-12-01             2015-12-31          531
 53563          673               0        1814        2015-11-01             2015-11-30          531

相反,我的结果看起来像

 DisbursementID ServiceProviderID Original CircuitID   Date Range  Amount
 -------------- ----------------- -------- ----------- ---------- -------
 53562          673               0        1814        2015-12-01 531
 53562          673               0        1814        2015-12-02 531
 53562          673               0        1814        2015-12-03 531
 53562          673               0        1814        2015-12-04 531
 53562          673               0        1814        2015-12-05 531
 53563          673               0        1814        2015-11-01 531
 53563          673               0        1814        2015-11-02 531
 53563          673               0        1814        2015-11-03 531
 53563          673               0        1814        2015-11-04 531
 53563          673               0        1814        2015-11-05 531

以下是我发现的一段代码,但可以根据我的需要进行调整。它几乎解决了我的问题,但我无法弄清楚如何在我的第一条记录中包含范围日期块。我知道为什么会这样做,但不知道如何正确修复它:

 DisbursementID ServiceProviderID Original CircuitID   Date Range  Amount
 -------------- ----------------- -------- ----------- ---------- -------
 53562          673               0        1814        2015-12-01 531
 53563          673               0        1814        2015-11-01 531
 53563          673               0        1814        2015-11-02 531
 53563          673               0        1814        2015-11-03 531
 53563          673               0        1814        2015-11-04 531
 53563          673               0        1814        2015-11-05 531

2 个答案:

答案 0 :(得分:2)

如果您没有或不能使用Tally / Calendar Table,另一种方法是使用ad-hoc计数表。

Declare @YourTable table (DisbursementID int, ServiceProviderID int, Original int, CircuitID int, Beginning_Service_Date date, Ending_Service_Date date, Amount int)
Insert Into @YourTable values
 ( 53562,673,0,1814,'2015-12-01','2015-12-31',531)
,( 53563,673,0,1814,'2015-11-01','2015-11-30',531)

;with cte1 as (
                 Select MinDate=min(Beginning_Service_Date)
                       ,MaxDate=max(Ending_Service_Date)
                 From @YourTable )
     ,cte2 as (
                 Select Top (DateDiff(DD,(select MinDate from cte1),(select MaxDate from cte1))+1) 
                        D = DateAdd(DD,-1+Row_Number() Over (Order By (Select null)),(select MinDate from cte1)) 
                  From  master..spt_values A -- ,master..spt_values B  -- If you need more than 6 years
 )
Select A.DisbursementID 
      ,A.ServiceProviderID 
      ,A.Original 
      ,A.CircuitID 
      ,[Date Range] = B.D
      ,A.Amount
 From  @YourTable A
 Join cte2 B on B.D between A.Beginning_Service_Date and A.Ending_Service_Date

答案 1 :(得分:1)

使用递归cte是生成一系列日期的最糟糕方法之一。 John Cappelletti 的答案是much better,用于按需生成日期范围而不是使用递归cte。

如果您打算在55,000多行中使用它,并且您将不止一次地运行此类操作,那么最好只创建一个DatesCalendar表。

内存中只有152kb,你可以在一张桌子上有30年的日期,你可以像这样使用它:

/* 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]);

并使用Dates表,如下所示:

select 
    t.DisbursementID
  , t.ServiceProviderID
  , t.Original
  , t.CircuitID
  , d.[date]
  , t.Amount
from t
  inner join dates d
    --on d.date >= t.Beginning_Service_Date
   --and d.date <= t.Ending_Service_Date
  /* if you want to have the date range work when 
      Beginning_Service_Date and Ending_Service_Date are backwards
      you could use between */
    on d.date between t.Beginning_Service_Date
                  and t.Ending_Service_Date

rextester演示:http://rextester.com/WNMJW41879

数字和日历表参考: