Thomas Steven
Thomas Steven

Reputation: 449

SQL grouping based on order and value

I have a table

loctype    order
ACUTE      1
ACUTE      2
COM        3
COM        4
ACUTE      5
COM        6

I want a query that will apply rankings to groups in order, so my desired outcome is:

loctype    order    group_order
ACUTE      1        1
ACUTE      2        1
COM        3        2
COM        4        2
ACUTE      5        3
COM        6        4

Is there a way to do this as a SQL query without resorting to cursors?

Upvotes: 0

Views: 1054

Answers (2)

Thomas Steven
Thomas Steven

Reputation: 449

I tried the given solution for SQL Server 2008 (that's what I have to work with). Unfortunately it didn't give quite the correct results, however working from Gordon's example, I came up with this, which does give exactly the desired result.

SELECT 
    *
FROM
    (       
        SELECT
            *,
            DENSE_RANK() over(order by (SELECT ISNULL(MAX(#tmp.[order]),0) FROM #tmp WHERE #tmp.[order]<t.[order] AND #tmp.loctype <> t.loctype)) as intorder
        FROM
            #tmp AS t
    ) AS u

This gives

loctype order group_order
ACUTE   1     1
ACUTE   2     1
COM     3     2
COM     4     2
ACUTE   5     3
COM     6     4

Essentially it hides an initial ordering inside the DENSE_RANK(). Without the DENSE_RANK() it looks like this:

SELECT * FROM (
SELECT *, (SELECT ISNULL(MAX(#tmp.[order]),0) FROM #tmp WHERE #tmp.[order] t.loctype) as intgroup FROM #tmp AS t ) AS u

And gives this result:

loctype order   intgroup
ACUTE   1       0
ACUTE   2       0
COM     3       2
COM     4       2
ACUTE   5       4
COM     6       5

The interim group order can then be DENSE_RANKed to give the desired outcome.

Upvotes: 0

Gordon Linoff
Gordon Linoff

Reputation: 1269753

One method for achieving this is a difference of row_number() to identify the groups and then dense_rank() on the minimum value. The code looks like:

select t.*, dense_rank(minid) over (order by minid) as group_order
from (select t.*, min(id) over (partition by loctype, grp) as minid
      from (select t.*
                   (row_number() over (order by [order]) -
                    row_number() over (partition by loctype order by [order])
                   ) as grp
            from t
           ) t
     ) t;

Another method (for SQL Server 2012+) is to use lag() with a cumulative sum:

select t.*,
       sum(case when loctype = prev_loctype then 0 else 1 end) over
           (order by id) as group_order
from (select t.*, lag(loctype) over (order by id) as prev_loctype
      from t
     ) t

Upvotes: 1

Related Questions