Search code examples
sql-servergroupingwindow-functions

TSQL - Difficult Grouping


Please see fiddle: http://sqlfiddle.com/#!6/e6768/2

I have data, like below:

DRIVER  DROP
1       1
1       2
1       ReturnToBase
1       4
1       5
1       ReturnToBase
1       6
1       7    
2       1
2       2
2       ReturnToBase
2       4

I am trying to group my data, so for each driver, each group of return to bases have a grouping number.

My output should look like this:

DRIVER      DROP           GROUP
1            1              1
1            2              1
1            ReturnToBase   1
1            4              2
1            5              2
1            ReturnToBase   2
1            6              3
1            7              3
1            ReturnToBase   3               
2            1              1           
2            2              1
2            ReturnToBase   1   
2            4              2

I've tried getting this result with a combination of windowed functions but I've been miles off so far

Below is what I had so far, it isn't supposed to be functional I was trying to figure out how it could be done, if it's even possible.

SELECT 
    ROW_NUMBER() OVER (Partition BY Driver order by Driver Desc) rownum,
    Count(1) OVER (Partition By Driver Order By Driver Desc) counter,
    Count
    DropNo,
    Driver,
    CASE DropNo 
       WHEN 'ReturnToBase' THEN 1 ELSE 0 END AS EnumerateRound
FROM 
    Rounds

Solution

  • You can use the following query:

    SELECT id, DRIVER, DROPno, 
           1 + SUM(flag) OVER (PARTITION BY DRIVER ORDER BY id) -
           CASE 
              WHEN DROPno = 'ReturnToBase' THEN 1 
              ELSE 0 
           END AS grp       
    FROM (
      SELECT id, DRIVER, DROPno, 
             CASE 
                WHEN DROPno = 'ReturnToBase' THEN 1 
                ELSE 0 
             END AS flag
      FROM rounds ) AS t
    

    Demo here

    This query uses windowed version of SUM with ORDER BY in the OVER clause to calculate a running total. This version of SUM is available from SQL Server 2012 onwards AFAIK.

    Fiddling a bit with this running total value is all we need in order to get the correct GROUP value.

    EDIT: (credit goes to @Conrad Frix)

    Using CROSS APPLY instead of an in-line view can considerably simplify things:

    SELECT id, DRIVER, DROPno, 
           1 + SUM(x.flag) OVER (PARTITION BY DRIVER ORDER BY id) - x.flag
    FROM rounds
    CROSS APPLY (SELECT CASE WHEN DROPno = 'ReturnToBase' THEN 1 ELSE 0 END) AS x(flag)
    

    Demo here