【问题标题】:Finding continuity with records that denote periods查找表示期间的记录的连续性
【发布时间】:2015-10-23 10:27:54
【问题描述】:

我有一张表,表示责任期,类似于以下内容:

Key     Resp_Key      Start_Date      End_Date
1       150           2015-01-01      2015-01-25
2       150           2015-01-25      2015-02-15
3       200           2015-02-15      2015-03-01
4       150           2015-03-01      2015-04-30
5       150           2015-04-30      NULL

当某人当前负责时,End_Date 列是NULL。对于上面的例子,Resp_Key150 的一方自 3 月 1 日起负责(其他列可能在 4 月 30 日发生了变化,导致为该日期创建了另一条记录,但这些对于这个问题并不重要)。

我需要创建一个查询来查找连接到当前期间和Resp_Key 的所有连续记录。在上面的例子中,查询应该返回记录 4 和 5,因为记录 5 是当前期间,并且记录 4 与之相连(因为记录 4 的 End_Date 是记录 5 的 Start_Date,它们都有相同Resp_Key)。不会包括记录 3,因为 Resp_Key 不同,并且记录 1 和 2 也不会包括在内,因为它们与当前记录不连续。

如果进行了以下更改/添加:

Key     Resp_Key      Start_Date      End_Date
5       150           2015-04-30      2015-05-31
6       231           2015-05-31      2015-06-30
7       150           2015-06-30      NULL

查询应该只返回记录 7,因为它是当前记录,并且紧接在它之前的时间段有不同的 Resp_Key

我一直在尝试编写 CTE 来处理此问题,但我不确定这是正确的工具。递归 CTE 是有意义的,因为我无法事先知道有多少记录是我感兴趣的连续周期的一部分。但我编写的 CTE 告诉我不能使用 LEFT JOIN,而我没有对于只有一个记录在最近连续周期的情况,不知道如何解决对它的需求。

;WITH ContinuousPeriod
AS (
    -- Current Period and key of immediately preceding period
    SELECT Current.Key, Current.Start_Date, Current.End_Date, Previous.Key AS PrevKey
    FROM PeriodTable AS Current
    LEFT JOIN PeriodTable AS Previous
        ON Previous.End_Date = Current.Start_Date
        AND Previous.Resp_Key = Current.Resp_Key
    WHERE Current.PEVNT_END_DATE IS NULL    
    UNION ALL
    . . .

有什么建议吗?

【问题讨论】:

    标签: sql sql-server-2008 recursive-query


    【解决方案1】:

    如果我理解正确,我相信您可以改用ROW_NUMBER(),它在 SQL Server 2008 中受支持,如下所示:

    ;WITH cte
    AS (
        SELECT [Key]
            ,Resp_Key
            ,Start_Date
            ,End_Date
            ,row_number() OVER (
                PARTITION BY Resp_Key ORDER BY Start_Date
                ) as rn
        FROM test
        )
    SELECT 
         cte.[Key]
        ,cte.Resp_Key
        ,cte.Start_Date
        ,cte.End_Date
    FROM cte 
    join cte c1 on c1.start_date = cte.end_date 
                and c1.rn= cte.rn+1 
                and c1.end_date is null
     UNION ALL
     select 
         cte.[Key]
        ,cte.Resp_Key
        ,cte.Start_Date
        ,cte.End_Date
     from cte 
     where cte.end_date is null
     order by cte.Resp_Key,cte.Start_Date;
    

    SQL Fiddle Demo

    【讨论】:

      【解决方案2】:

      关键是找到连续时间序列的开始位置。当开始日期的lag() 不合适时会发生这种情况。然后,您需要最后一个之后的所有记录。所以:

      with t as (
            select t.*,
                   lag(end_date) over (partition by resp_key order by start_date) as prev_end_date
            from test t
            where resp_key = (select resp_key from test t where end_date is null)
           ),
           tt as (
            select resp_key, max(start_date) as last_sequence_start
            from t
            where prev_end_date is null or prev_end_date <> start_date
            group by resp_key
           )
      select t.*
      from test t join
           tt
           on tt.resp_key = t.resp_key
      where t.start_date >= tt.last_sequence_start;
      

      Here 是一个 SQL Fiddle。

      【讨论】:

      • 我没有听说过 LAG 功能,如果我没有被困在 SQL Server 2008 中,它看起来可以解决我的问题。
      【解决方案3】:

      如果你使用Sql server 2012+,那么你可以使用LEAD/LAG窗口函数来检查连续性

      ;WITH cte 
           AS (SELECT [key], 
                      resp_key, 
                      start_date, 
                      end_date, 
                      lag_ed=Lag(end_date)OVER(partition BY resp_key 
                                 ORDER BY start_date) 
               FROM   test) 
      SELECT * 
      FROM   cte 
      WHERE  start_date = lag_ed 
              OR end_date IS NULL 
      

      SQLFIDDLE DEMO

      如果您使用的是 sql server 2008 或更低版本,那么您需要为每个 resp_key 生成行号,然后自行加入结果以检查连续性

      【讨论】:

      • 我正在研究你为陷入 SQL Server 2008 的人描述的变体。
      猜你喜欢
      • 2021-02-20
      • 2013-01-23
      • 2018-12-01
      • 2014-03-02
      • 2019-02-16
      • 1970-01-01
      • 1970-01-01
      • 2021-08-20
      • 2017-05-02
      相关资源
      最近更新 更多