Ranking table on one column whilst sorting on another

Viewed 87

I have a subset of a SQL Server 2008 R2 table like this:

cust_id | prod_id | day | price
--------+---------+-----+-------
137656    194528   42373   9.11
137656    194528   42374   9.11
137656    194528   42375   9.61
137656    194528   42376   9.61
137656    194528   42377   9.11
137656    194528   42378   9.11

I need to rank the different price periods like so:

cust_id | prod_id | day | price | rank
--------+---------+-----+-------+------
137656    194528   42373   9.11     1
137656    194528   42374   9.11     1
137656    194528   42375   9.61     2
137656    194528   42376   9.61     2
137656    194528   42377   9.11     3
137656    194528   42378   9.11     3

so that it sorts by cust_id, prod_id and day ascending but increments the rank when the price changes. I have tried to use DENSE_RANK() like this:

SELECT 
    cust_id, prod_id, [day], price, 
    DENSE_RANK() OVER (ORDER BY cust_id, prod_id, price)
FROM
    @prices 

This returns something like:

cust_id | prod_id | day | price | rank
--------+---------+-----+-------+------
137656    194528   42373   9.11     1
137656    194528   42374   9.11     1
137656    194528   42377   9.11     1
137656    194528   42378   9.11     1
137656    194528   42375   9.61     2
137656    194528   42376   9.61     2

Obviously excluding the day from the sort will give me these results but whenever I include the day in the order by section of the DENSE_RANK() - it just partitions each new day as a new ID....

Does anyone have any ideas on how this should work? Appreciate any advice and can give more info if required

1 Answers

The first variant with LAG and SUM

SELECT
  *,
  1+SUM(IncCount)OVER(PARTITION BY cust_id ORDER BY [day]) [rank]
  --1+SUM(IncCount)OVER(PARTITION BY cust_id ORDER BY [day] ROWS BETWEEN unbounded preceding AND current row) [rank]
FROM
  (
    SELECT
      *,
      IIF(LAG(price)OVER(PARTITION BY cust_id ORDER BY [day])<>price,1,0) IncCount
      --CASE WHEN LAG(price)OVER(PARTITION BY cust_id ORDER BY [day])<>price THEN 1 ELSE 0 END IncCount
    FROM Test
  ) q

The second variant without LAG

WITH numCTE AS(
  SELECT *,ROW_NUMBER()OVER(PARTITION BY cust_id ORDER BY [day]) RowNum
  FROM Test
)
SELECT
  t1.*,
  1+SUM(CASE WHEN t2.price<>t1.price THEN 1 ELSE 0 END)OVER(PARTITION BY t1.cust_id ORDER BY t1.[day]) [rank]
  --1+SUM(CASE WHEN t2.price<>t1.price THEN 1 ELSE 0 END)OVER(PARTITION BY t1.cust_id ORDER BY t1.[day] ROWS BETWEEN unbounded preceding AND current row) [rank]
FROM numCTE t1
LEFT JOIN numCTE t2 ON t2.RowNum+1=t1.RowNum AND t2.cust_id=t1.cust_id

The third variant with recursive CTE

WITH numCTE AS(
  SELECT *,ROW_NUMBER()OVER(PARTITION BY cust_id ORDER BY [day]) RowNum
  FROM Test
),
rankCTE AS(
  SELECT RowNum,cust_id,prod_id,[day],price,1 [rank]
  FROM numCTE
  WHERE RowNum=1

  UNION ALL

  SELECT
    n.RowNum,n.cust_id,n.prod_id,n.[day],n.price,
    r.[rank]+CASE WHEN n.price<>r.price THEN 1 ELSE 0 END [rank]
  FROM numCTE n
  JOIN rankCTE r ON n.RowNum=r.RowNum+1 AND n.cust_id=r.cust_id
)
SELECT *
FROM rankCTE
OPTION(MAXRECURSION 0)
Related