Search code examples
sqloracle-databaserankingrank

Oracle SQL conditional ranking


In my query, I am doing multiple types of ranking and for one of ranking types, I want to rank the row only if certain column is not null. Else I don't want ranking to happen.

For example here's a sample table:

+------+------------+------------+--------+--------+
| col1 |    col2    |    col3    | rank 1 | rank 2 |
+------+------------+------------+--------+--------+
| a    | 2018-01-20 | 2018-03-04 |      2 | 2      |
| a    | 2018-01-24 | 2018-04-04 |      1 | 1      |
| b    | 2018-01-02 | 2018-05-03 |      1 | 1      |
| c    | 2017-01-02 | 2017-05-08 |      3 | 2      |
| d    | 2016-05-24 | null       |      1 | null   |
| c    | 2018-02-05 | 2018-05-03 |      2 | 1      |
| c    | 2018-07-28 | null       |      1 | null   |
+------+------------+------------+--------+--------+

rank1 is calculated alright based on partition by col1 order by col2 desc rank 2 should be calculated the same way, but only when when col3 is null, else it should be null.

How can I achieve both ranks in a single query? I tried to use case statement for rank2, but it skips the ranking when col3 is null,


Solution

  • If I understand corrcly, you can try to use CASE WHEN with sum window function

    CASE WHEN check col3 isn't null do accumulate else display NULL

    CREATE TABLE T(
      col1 VARCHAR(5),
      col2 DATE,
      col3 DATE
    );
    
    INSERT INTO T VALUES ( 'a' , to_date('2018-01-20','YYYY-MM-DD') , to_date('2018-03-04','YYYY-MM-DD'));  
    INSERT INTO T VALUES ( 'a' , to_date('2018-01-24','YYYY-MM-DD') , to_date('2018-04-04','YYYY-MM-DD'));  
    INSERT INTO T VALUES ( 'b' , to_date('2018-01-02','YYYY-MM-DD') , to_date('2018-05-03','YYYY-MM-DD'));  
    INSERT INTO T VALUES ( 'c' , to_date('2017-01-02','YYYY-MM-DD') , to_date('2017-05-08','YYYY-MM-DD'));  
    INSERT INTO T VALUES ( 'd' , TO_DATE('2016-05-24','YYYY-MM-DD') , null);  
    INSERT INTO T VALUES ( 'c' , TO_DATE('2018-02-05','YYYY-MM-DD') , to_date('2018-05-03','YYYY-MM-DD'));  
    INSERT INTO T VALUES ( 'c' , TO_DATE('2018-07-28','YYYY-MM-DD') , null);  
    

    Query 1:

    select t1.*,
        rank() OVER(partition by col1 order by col2 desc) rank1,
        (CASE WHEN COL3 IS NOT NULL THEN
           SUM(CASE WHEN COL3 IS NOT NULL THEN 1 ELSE 0 END) OVER(partition by col1 order by col2 desc)
        ELSE
           NULL
        END) rank2
    FROM T t1
    

    Results:

    | COL1 |                 COL2 |                 COL3 | RANK1 |  RANK2 |
    |------|----------------------|----------------------|-------|--------|
    |    a | 2018-01-24T00:00:00Z | 2018-04-04T00:00:00Z |     1 |      1 |
    |    a | 2018-01-20T00:00:00Z | 2018-03-04T00:00:00Z |     2 |      2 |
    |    b | 2018-01-02T00:00:00Z | 2018-05-03T00:00:00Z |     1 |      1 |
    |    c | 2018-07-28T00:00:00Z |               (null) |     1 | (null) |
    |    c | 2018-02-05T00:00:00Z | 2018-05-03T00:00:00Z |     2 |      1 |
    |    c | 2017-01-02T00:00:00Z | 2017-05-08T00:00:00Z |     3 |      2 |
    |    d | 2016-05-24T00:00:00Z |               (null) |     1 | (null) |