我的表中有以下记录,名为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
答案 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多行中使用它,并且您将不止一次地运行此类操作,那么最好只创建一个Dates
或Calendar
表。
内存中只有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
数字和日历表参考: