简体   繁体   中英

Assign Ranks to records based on condition

I have a table with following records:

=======================
| ID    |  device_num |
=======================
|  1    |      11     |
|  2    |      11     | 
|  3    |      NULL   |
|  4    |      11     |
|  5    |      11     |
|  6    |      NULL   |
|  7    |      11     |
|  8    |      12     |
|  9    |      12     |
|  10   |      13     |
|  11   |      NULL   | 
|  12   |      13     |
|  13   |      13     |
|  14   |      13     |
|  15   |      14     |
|  16   |      14     |
=======================

I want to assign a rank to each device number based on following cases:

1- Rank should be started with 1.

2- Each record should be compared with its previous record if both has the same device number then assign a same rank to records.

3- Do not assign rank to NULL records. In case if we get same device number after the Null record then rank should be increased by one.

4- If device number not matches with previous record then increase rank by 1.

Desired output:

===============================
| ID    | RANK  |  device_num |
===============================
|  1    |  1    |      11     |
|  2    |  1    |      11     | 
|  3    |       |      NULL   |
|  4    |  2    |      11     |
|  5    |  2    |      11     |
|  6    |       |      NULL   |
|  7    |  3    |      11     |
|  8    |  4    |      12     |
|  9    |  4    |      12     |
|  10   |  5    |      13     |
|  11   |       |      NULL   | 
|  12   |  6    |      13     |
|  13   |  6    |      13     |
|  14   |  6    |      13     |
|  15   |  7    |      14     |
|  16   |  7    |      14     |
===============================

I have tried with PostgreSQL Rank functions dense , rank etc but not able to assign rank in this order.

Try this:

SELECT id, device_num, 
       CASE 
         WHEN device_num IS NOT NULL 
         THEN DENSE_RANK() OVER (ORDER BY CASE 
                                            WHEN device_num IS NOT NULL
                                            THEN min_id 
                                          END) 
       END AS RANK
FROM (
  SELECT id, device_num, 
         MIN(id) OVER (PARTITION BY device_num, grp) AS min_id
  FROM (
  SELECT id, device_num,
         ROW_NUMBER() OVER (ORDER BY id) - 
         ROW_NUMBER() OVER (PARTITION BY CASE 
                                           WHEN device_num IS NULL THEN -1 
                                           ELSE device_num
                                         END
                            ORDER BY id) AS grp
  FROM mytable) AS t) AS s
ORDER BY id

I've made the assumptions that:

  1. there is an auto increment id field in your table that specifies row order.
  2. device_num field is of integer type and -1 is not a valid value for the field.

Demo here

I write a query like this:

;WITH t1 AS (
SELECT *
    , ROW_NUMBER() OVER (ORDER BY (SELECT null)) AS rn
FROM yourTable
) -- To add a row-number to yourTable
, t2 AS (
SELECT *
    , CASE 
        WHEN device_num IS NULL THEN 0
        WHEN ISNULL(LAG(device_num) OVER (ORDER BY rn), -1) <> ISNULL(device_num, -1) THEN 1 
        ELSE 0 
    END AS willChange
FROM t1
)
SELECT device_num
    , CASE 
        WHEN device_num IS NULL THEN null
        ELSE SUM(willChange) OVER (ORDER BY rn)
    END [RANK]
FROM t2;

EDIT : By using ID : it can be like this:

;WITH t AS (
SELECT *
    , CASE 
        WHEN device_num IS NULL THEN 0
        WHEN ISNULL(LAG(device_num) OVER (ORDER BY ID), -1) <> ISNULL(device_num, -1) THEN 1 
        ELSE 0 
    END AS willChange
FROM yourTable
)
SELECT device_num
    , CASE 
        WHEN device_num IS NULL THEN null
        ELSE SUM(willChange) OVER (ORDER BY ID)
    END [RANK]
FROM t;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM