如何有效地选择以前的非空值?

ada*_*mar 40 postgresql

我在Postgres有一张表,如下所示:

# select * from p;
 id | value 
----+-------
  1 |   100
  2 |      
  3 |      
  4 |      
  5 |      
  6 |      
  7 |      
  8 |   200
  9 |          
(9 rows)
Run Code Online (Sandbox Code Playgroud)

我想查询使它看起来像这样:

# select * from p;
 id | value | new_value
----+-------+----------
  1 |   100 |    
  2 |       |    100
  3 |       |    100
  4 |       |    100
  5 |       |    100
  6 |       |    100
  7 |       |    100
  8 |   200 |    100
  9 |       |    200
(9 rows)
Run Code Online (Sandbox Code Playgroud)

我已经可以在select中使用子查询执行此操作,但在我的实际数据中,我有20k或更多行,并且它变得非常慢.

这可以在窗口函数中做到吗?我喜欢使用lag(),但它似乎不支持IGNORE NULLS选项.

select id, value, lag(value, 1) over (order by id) as new_value from p;
 id | value | new_value
----+-------+-----------
  1 |   100 |      
  2 |       |       100
  3 |       |      
  4 |       |
  5 |       |
  6 |       |
  7 |       |
  8 |   200 |
  9 |       |       200
(9 rows)
Run Code Online (Sandbox Code Playgroud)

ada*_*mar 74

我发现SQL Server的这个答案也适用于Postgres.从来没有做过,我认为这种技术非常聪明.基本上,他通过在嵌套查询中使用case语句为窗口函数创建自定义分区,该嵌套查询在值不为空时递增总和,否则单独留下它.这允许人们用与前一个非空值相同的数字来描述每个空部分.这是查询:

SELECT
  id, value, value_partition, first_value(value) over (partition by value_partition order by id)
FROM (
  SELECT
    id,
    value,
    sum(case when value is null then 0 else 1 end) over (order by id) as value_partition

  FROM p
  ORDER BY id ASC
) as q
Run Code Online (Sandbox Code Playgroud)

结果如下:

 id | value | value_partition | first_value
----+-------+-----------------+-------------
  1 |   100 |               1 |         100
  2 |       |               1 |         100
  3 |       |               1 |         100
  4 |       |               1 |         100
  5 |       |               1 |         100
  6 |       |               1 |         100
  7 |       |               1 |         100
  8 |   200 |               2 |         200
  9 |       |               2 |         200
(9 rows)
Run Code Online (Sandbox Code Playgroud)

  • 你可以使用`count(value)`而不是`sum(case ...)`作为count忽略空值. (10认同)
  • +1 ...这是一个非常聪明的解决方案,`id` 和 `value` 上的索引也将提高性能。 (3认同)

Slo*_*jic 7

您可以在Postgres中创建自定义聚合函数。这是该int类型的示例:

CREATE FUNCTION coalesce_agg_sfunc(state int, value int) RETURNS int AS
$$
    SELECT coalesce(value, state);
$$ LANGUAGE SQL;

CREATE AGGREGATE coalesce_agg(int) (
    SFUNC = coalesce_agg_sfunc,
    STYPE  = int);
Run Code Online (Sandbox Code Playgroud)

然后照常查询。

SELECT *, coalesce_agg(b) over w, sum(b) over w FROM y
  WINDOW w AS (ORDER BY a);

a b coalesce_agg sum 
- - ------------ ---
a 0            0   0
b ?            0   0
c 2            2   2
d 3            3   5
e ?            3   5
f 5            5  10
(6 rows)
Run Code Online (Sandbox Code Playgroud)


Mat*_*sOl 5

好吧,我不能保证这是最有效的方法,但有效:

SELECT id, value, (
    SELECT p2.value
    FROM p p2
    WHERE p2.value IS NOT NULL AND p2.id <= p1.id
    ORDER BY p2.id DESC
    LIMIT 1
) AS new_value
FROM p p1 ORDER BY id;
Run Code Online (Sandbox Code Playgroud)

以下索引可以改进大型数据集的子查询:

CREATE INDEX idx_p_idvalue_nonnull ON p (id, value) WHERE value IS NOT NULL;
Run Code Online (Sandbox Code Playgroud)

假设value稀疏(例如有很多空值)它将运行良好。