11

Using the following table:

A | B    | C    | ts
--+------+------+------------------
1 | null | null | 2016-06-15 10:00
4 | null | null | 2016-06-15 11:00 
4 |    9 | null | 2016-06-15 12:00
5 |    1 |    7 | 2016-06-15 13:00

How do I select the first non-null value of each column in a running window of N rows? "First" as defined by the order of timestamps in columns ts. Querying the above table would result in:

A | B | C
--+---+---
1 | 9 | 7
0

4 Answers 4

22

The window function first_value() allows for a rather short and elegant solution:

SELECT first_value(a) OVER (ORDER BY a IS NULL, ts) AS a
     , first_value(b) OVER (ORDER BY b IS NULL, ts) AS b
     , first_value(c) OVER (ORDER BY c IS NULL, ts) AS c
FROM   t
LIMIT  1;

a IS NULL evaluates to TRUE or FALSE. FALSE sorts before TRUE. This way, non-null values come first. Order by ts (timestamp column like you commented) next and you've got it in a single SELECT.

This would be simpler if Postgres supported IGNORE NULLS. The manual:

The SQL standard defines a RESPECT NULLS or IGNORE NULLS option for lead, lag, first_value, last_value, and nth_value. This is not implemented in PostgreSQL: the behavior is always the same as the standard's default, namely RESPECT NULLS.

One of the few omissions with regard to standard SQL in this area.

We also cannot use a FILTER clause. See:

The manual:

Only window functions that are aggregates accept a FILTER clause.

db<>fiddle here
Old sqlfiddle

Sign up to request clarification or add additional context in comments.

2 Comments

Is there any advantage over ORDER BY a IS NULL vs. order by a nulls first?
@a_horse_with_no_name: NULLS LAST in this case. But neither is equivalent. ORDER BY a IS NULL only sorts NULL values last, the rest remains unsorted - to be sorted by ts in the next iteration. ORDER BY a NULLS LAST would do too much.
4

You should define an order (a primary key or something else) to get the FIRST not null value. So I used ID column to order rows in your table.

select 
(select A from t where A is not null ORDER BY id LIMIT 1),
(select b from t where b is not null ORDER BY id LIMIT 1),
(select c from t where c is not null ORDER BY id LIMIT 1)

SQLFiddle demo

Comments

2

You can do this with window functions. I have Partitioned the result into 2 parts and then used this partition for the row_number window function

  1. having null values
  2. having a valid valid

Then, using a basic case to get the ones having the row_number as 1 and which have a not null value in them

SQLFIDDLE

SELECT
    max ( CASE
            WHEN a_row_num = 1 AND a IS NOT NULL THEN a
          END ) AS A,
    max ( CASE
            WHEN b_row_num = 1 AND B IS NOT NULL THEN B
          END ) AS B,
    max ( CASE
            WHEN c_row_num = 1 AND C IS NOT NULL THEN C
          END ) AS C
FROM
    (
      SELECT
          a,
          row_number ( ) over ( partition BY a IS NULL ORDER BY ID ) a_row_num,
          b,
          row_number ( ) over ( partition BY b IS NULL ORDER BY ID ) b_row_num,
          c,
          row_number ( ) over ( partition BY c IS NULL ORDER BY ID ) c_row_num
      FROM
          test
    ) AS sub_query

Output:

| A | B | C |
|---|---|---|
| 1 | 9 | 7 |

NOTE: I have added a id field, which helps to know which of the records was first inserted, we use it in ascending order within our window function

Comments

0

Not sure if I got the question correctly
as it seems quite simple basically.
Try this query.

SQL Fiddle: http://sqlfiddle.com/#!11/ac585/8

WITH t0 AS
(
    SELECT A FROM
    TableName t0
    WHERE (A IS NOT NULL)
    ORDER BY ID ASC
    LIMIT 1
),

t1 AS
(
    SELECT B FROM
    TableName
    WHERE (B IS NOT NULL)
    ORDER BY ID ASC
    LIMIT 1
),

t2 AS

(
    SELECT C FROM
    TableName
    WHERE (C IS NOT NULL)
    ORDER BY ID ASC
    LIMIT 1
)

SELECT t0.A, t1.B, t2.C
FROM
t0
JOIN t1 ON 1=1
JOIN t2 ON 1=1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.