【问题标题】:Seperate Records to share Date Range utilizing a single column of dates使用单列日期分隔记录以共享日期范围
【发布时间】:2017-08-09 08:54:50
【问题描述】:

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

 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);

【问题讨论】:

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


    【解决方案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

    数字和日历表参考:

    【讨论】:

    • 你说得对,这是一个巨大的性能问题。我确实注意到了。他(约翰)确实问我是否有日历类型的桌子,而我没有。所以他没有基于此提供他的初步答案。另外,我不知道如何创建一个以针对我提供的代码正确使用它。但你做到了,并给了我另一种看待这个问题的方式。我非常感谢。
    • @CharlesBernardes John 的回答很好(我也赞成),但是关于在 55,000 行以上使用它的评论是我提供这个替代方案的充分理由。
    • 我给了约翰答案,因为他确实给了我一个解决方案,根据我最初的帖子确实有效。既然你用了一个更好的选择来跟进它,我希望我也能给你一个答案。我希望你没有被冒犯。我也给了你一个赞成票。
    • @CharlesBernardes 一点也不,正如我之前所说的那样,约翰的回答很好。如果您发现我的回答值得考虑作为替代,那么点赞就足够了。
    【解决方案2】:

    如果您没有或不能使用计数/日历表,另一种方法是使用临时计数表。

    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
    

    【讨论】:

    • 谢谢。我没有 TALLY/CALENDAR 表,也不知道 CROSS APPLY 是如何工作的,但现在会调查一下。
    • 哦,我喜欢您编辑的另一个选项解决方案。我使用 SQL 代码的 ETL 过程在使用您的第一个解决方案时遇到了问题,但是您的编辑解决方案,它做得更好。
    • @CharlesBernardes 奇怪的是,您第一次遇到了问题。我得到了相同的结果。将 CROSS APPLY 放在您的后袋中。它们很有用......将它们视为子程序
    • 当我在 SSMS 中针对 55,000 条记录运行它时,它最终给了我一条错误消息 Msg 127, Level 15, State 1, Line 1 A TOP N or FETCH rowcount value may not benegative。跨度>
    • 我认为您的解决方案是有效的,直到我进行了一些测试。似乎并非所有记录都被退回。他们是我可以向您发送个人电子邮件的一种方式吗?否则,我不确定如何给您一份我表中所有记录的副本,以向您展示它没有返回的内容。我的表中有超过 50,000 条记录。我对你的代码理解不够,无法找出它为什么会这样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-18
    • 2013-03-23
    • 1970-01-01
    • 1970-01-01
    • 2018-07-01
    • 2019-10-25
    相关资源
    最近更新 更多