1

我從我的表中有以下記錄稱爲支付。我喜歡做的是根據開始和結束服務日期,利用僅1列日期將每條記錄分成單獨的記錄集。單獨記錄分享日期範圍利用單列日期

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 

下面是一段代碼,我發現,但適合我的需要。它幾乎解決了我的問題,但我不知道如何從我的第一個記錄中包含範圍日期塊。我知道爲什麼這樣做,但不知道如何正確地解決它:

;With Dates as 
( 
Select DisbursementID, ServiceProviderID,Original,CircuitID 
,Beginning_Service_Date as BeginDate, Ending_Service_Date as EndDate 
,Amount From Disbursement 

Union All 

Select DisbursementID, ServiceProviderID, Original,CircuitID 
,DATEADD(day,1,BeginDate) as CalenderDate, EndDate 
,Amount 
From Dates 
Where DATEADD(day,1,BeginDate) <= EndDate 
) 

Select DisbursementID, ServiceProviderID,Original,CircuitID 
,BeginDate as [Date Range], Amount from Dates 
Order By CircuitID 
Option (MAXRECURSION 366); 

回答

2

如果你沒有或者不能使用理貨/日曆表,另一種方法是使用一個AD-臨時理貨表。

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 
+0

謝謝。我沒有一個TALLY/CALENDAR表,我不知道CROSS APPLY是如何工作的,但現在就看看它。 –

+0

哦,我喜歡你編輯過的另一個選項解決方案。使用SQL代碼的我的ETL過程遇到了使用您的第一個解決方案的問題,但是您的EDITED解決方案使其更好。 –

+1

@CharlesBernardes奇怪的是,你有第一個問題。兩人都得到了相同的結果。將CROSS APPLY放在你的後袋裏。他們很有用...把它們當作一個子程序 –

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

號碼和日曆表參考:

+0

你是正確的,這是一個巨大的性能問題。我注意到了。他(約翰)確實問我是否有日曆類型的表格,但我沒有。所以他沒有提供他的初步答案。另外,我不知道如何創建一個適當的使用它來對付我提供的代碼。 但你做了,並給了我另一種看待這個問題的方式。我非常欣賞這一點。 –

+1

@CharlesBernardes John的答案是一個很好的答案(我贊成這個答案),但關於使用超過55,000行的評論對我來說是一個足夠的理由來提供這種選擇。 – SqlZim

+0

我給了約翰答案的要點,因爲他確實給了我一個根據我的初始職位工作的解決方案。既然你跟上了一個更好的選擇,我希望我也可以給你一個答案。我希望你不要生氣。我也給你一個upvote。 –