15

Lets say I have the following table:

 | User_id |   COL1   | COL2 |
 +---------+----------+------+
 | 1       |          | 1    |
 | 1       |          | 2    | 
 | 1       |   2421   |      | 
 | 1       |          | 1    | 
 | 1       |   3542   |      | 
 | 2       |          | 1    |

I need another column indicating the next non-null COL1 value for each row, so the result would look like the below:

 | User_id |   COL1   | COL2 | COL3 |
 +---------+----------+------+------
 | 1       |          | 1    | 2421 |
 | 1       |          | 2    | 2421 |
 | 1       |   2421   |      |      |
 | 1       |          | 1    | 3542 |
 | 1       |   3542   |      |      |
 | 2       |          | 1    |      |

SELECT 
first_value(COL1 ignore nulls) over (partition by user_id order by COL2 rows unbounded following) 
FROM table;

would work but I'm using PostgreSQL which doesn't support the ignore nulls clause.

Any suggested workarounds?

Michel Milezzi
  • 10,087
  • 3
  • 21
  • 36
user3558238
  • 151
  • 1
  • 1
  • 3

5 Answers5

17

You can still do it with windowing function if you add a case when criteria in the order by like this:

select
   first_value(COL1) 
   over (
     partition by user_id 
     order by case when COL1 is not null then 0 else 1 end ASC, COL2 
     rows unbounded following
   ) 
from table

This will use non null values first.

However performance will probably not be great compared to skip nulls because the database will have to sort on the additional criteria.

ChrisGPT was on strike
  • 127,765
  • 105
  • 273
  • 257
Sebastien
  • 5,506
  • 4
  • 27
  • 37
8

I also had the same problem. The other solutions may work, but I have to build multiple windows for each row I need.

You can try this snippets : https://wiki.postgresql.org/wiki/First/last_(aggregate)

If you create the aggregates you can use them:

SELECT 
first(COL1) over (partition by user_id order by COL2 rows unbounded following) 
FROM table;
1

There is always the tried and true approach of using a correlated subquery:

select t.*,
       (select t2.col1
        from t t2
        where t2.id >= t.id and t2.col1 is not null
        order by t2.id desc
        fetch first 1 row only
       ) as nextcol1
from t;
Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786
-1

Hope this helps,

SELECT * FROM TABLE ORDER BY COALESCE(colA, colB);

which orders by colA and if colA has NULL value it orders by colB.

Ashwaq
  • 431
  • 7
  • 17
-1

You can use COALESCE() function. For your query:

SELECT 
first_value(COALESCE(COL1)) over (partition by user_id order by COL2 rows unbounded following) 
FROM table;

but i don't understand what the reason to use sort by COL2, because this rows has null value for COL2:

 | User_id |   COL1   | COL2 |
 +---------+----------+------+
 | 1       |          | 1    |
 | 1       |          | 2    | 
 | 1       |   2421   |      | <<--- null?
 | 1       |          | 1    | 
 | 1       |   3542   |      | <<--- null?
 | 2       |          | 1    |
HEX
  • 1,647
  • 2
  • 15
  • 29