Search code examples
sql-server-2008-r2overlapping

Updating a table to avoid overlapping dates


I am trying to write a query that reorders date ranges around particular spans. It should do something that looks like this

Member   Rank   Begin Date   End Date
2275     A      9/9/14       11/17/14
2275     B      9/26/14      3/24/15
2275     B      3/25/15      12/31/15
8983     A      9/16/13      3/10/15
8983     B      2/24/15      4/28/15
8983     A      4/28/15      12/31/15

and have it become

Member   Rank   Begin Date   End Date
2275     A      9/9/14       11/17/14
2275     B      11/18/14     3/24/15
2275     B      3/25/15      12/31/15
8983     A      9/16/13      3/10/15
8983     B      3/11/15      4/27/15
8983     A      4/28/15      12/31/15

To explain further, I am looking to update the dates. There isn't much to the ranking except A > B. And there is only A and B. Date ranges with rank A should remain untouched. Overlapping B ranked dates are okay. I am concerned with B ranked dates overlapping with A ranked dates. The table is very large (~700 members) and with several different members IDs. The 2nd line (Rank B) of member 2275 changes the begin date to 11/18/15 to not overlap with the 1st line.

I am using Microsoft SQL Server 2008 R2

Thanks


Solution

  • LATEST EDIT: Here's what I did for pre-2012. I don't think it's the most elegant solution.

    WITH a AS (
        SELECT
            1 AS lgoffset
            , NULL AS lgdefval
            , ROW_NUMBER() OVER(PARTITION BY [Member] ORDER BY [Begin Date]) AS seq
            , [Member]
            , [Rank]
            , [Begin Date]
            , [End Date]
        FROM #table 
    )
    SELECT
        a.seq
        , a.[Member]
        , a.[Rank]
        , a.[Begin Date]
        , CASE
            WHEN a.[Rank] = 'B' AND a.[Begin Date] <= ISNULL(aLag.[End Date], a.lgdefval)
            THEN ISNULL(aLag.[End Date], a.lgdefval)
            ELSE a.[Begin Date]
          END AS bdate2
        , a.[End Date]
    INTO #b
    FROM a
    LEFT OUTER JOIN a aLag
        ON a.seq = aLag.seq + a.lgoffset
        AND a.[Member] = aLag.[Member]
    ORDER BY [Member], [Begin Date];
    
    
    UPDATE #table
    SET #table.bdate = CASE
                        WHEN #table.rnk = 'B' AND #table.bdate <= (SELECT #b.bdate2 FROM #b WHERE #b.bdate2 > #b.bdate and #table.mbr = #b.mbr)
                        THEN dateadd(d, 1,(SELECT bdate2 FROM #b WHERE #b.bdate2 > #b.bdate and #table.mbr = #b.mbr )) 
                        ELSE #table.bdate
                        END  
    

    EDIT PS: Below was my previous answer that only applies to 2012 and later.

    You may want to try the following SELECT statement to see if you get the desired results and then convert to an UPDATE:

    SELECT 
    [Member]
    , [Rank]
    , CASE
        WHEN [Rank] = 'B' AND [Begin Date] <= LAG([End Date],1,'12/31/2030') OVER(PARTITION BY [Member] ORDER BY [Begin Date]) 
        THEN DATEADD(d,1,LAG([End Date],1,'12/31/2030')OVER(PARTITION BY [Member] ORDER BY [Begin Date]))
        ELSE [Begin Date]
      END AS [Begin Date]
    , [End Date]
    FROM #Table
    ORDER BY [Member], [Begin Date]
    

    EDIT: So in order to update the begin date column:

    UPDATE #Table
    SET [Begin Date] = (SELECT
                        CASE
                            WHEN [Rank] = 'B' AND [Begin Date] <= LAG([End Date],1,'12/31/2030') OVER(PARTITION BY [Member] ORDER BY [Begin Date]) 
                            THEN DATEADD(d,1,LAG([End Date],1,'12/31/2030')OVER(PARTITION BY [Member] ORDER BY [Begin Date]))
                            ELSE [Begin Date]
                          END AS [Begin Date]
                        FROM #Table)
    

    EDIT 2: Some of my code was incorrect due to not realizing the lag function needed an OVER statement, updated select statement and update statement

    Sources:Alternate of lead lag function in sql server 2008

    http://blog.sqlauthority.com/2011/11/24/sql-server-solution-to-puzzle-simulate-lead-and-lag-without-using-sql-server-2012-analytic-function/