Postgresql 如何有效地选择前一个非空值?

Postgresql 如何有效地选择前一个非空值?,postgresql,Postgresql,我在Postgres中有一个表,如下所示: # select * from p; id | value ----+------- 1 | 100 2 | 3 | 4 | 5 | 6 | 7 | 8 | 200 9 | (9 rows) # select * from p; id | value | new_value ----+-------+-----

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

# select * from p;
 id | value 
----+-------
  1 |   100
  2 |      
  3 |      
  4 |      
  5 |      
  6 |      
  7 |      
  8 |   200
  9 |          
(9 rows)
# 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)
我想让它看起来像这样:

# select * from p;
 id | value 
----+-------
  1 |   100
  2 |      
  3 |      
  4 |      
  5 |      
  6 |      
  7 |      
  8 |   200
  9 |          
(9 rows)
# 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)
我已经可以通过select中的子查询来实现这一点,但在我的实际数据中,我有20k行或更多行,而且速度非常慢

在窗口函数中可以这样做吗?我很想使用lag(),但它似乎不支持该选项


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

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;
假设
是稀疏的(例如,有很多空值),它将正常运行。

我发现SQL Server也适用于Postgres。我以前从未做过,我认为这项技术相当聪明。基本上,他通过在嵌套查询中使用case语句为windowing函数创建一个自定义分区,该语句在值不为null时递增一个和,否则将其单独保留。这允许使用与前一个非空值相同的数字描绘每个空部分。问题是:

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
结果是:

 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)

您可以在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);
然后像往常一样询问

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)

您可以将最后一个_值与过滤器一起使用,以实现您需要的功能(至少在第9.4页中)


在我的情况下,我需要在非交易日保持一个连续的平衡,这只是周末,在非交易假日的情况下,偶尔是三天的周末

如果空天数非常少,可以通过CASE语句和一系列滞后窗口函数来解决此问题:

SELECT
    CASE
        WHEN balance IS NULL THEN
            -- A non-null balance must be found within the first 3 preceding rows
            CASE
                WHEN LAG(balance, 1) OVER () IS NOT NULL
                  THEN LAG(balance, 1) OVER ()
                WHEN LAG(balance, 2) OVER () IS NOT NULL
                  THEN LAG(d.balance, 2) OVER ()
                WHEN LAG(balance, 3) OVER () IS NOT NULL
                  THEN LAG(balance, 3) OVER ()
                END
        ELSE balance
    END
FROM daily_data;
对于无界问题不实用,但对于较小的间隙,这是一个很好的解决方案。如有必要,只需添加更多“WHEN LAG(,x)…”子句。我很幸运,我只需要用一个专栏就可以做到这一点,而且这个解决方案使我从我的目标中解脱出来

with p (id, value) as (
    values (1, 100),
           (2, null),
           (3, null),
           (4, null),
           (5, null),
           (6, null),
           (7, null),
           (8, 200),
           (9, null))
select *
     , (json_agg(value) filter (where value notnull) over (order by id) ->> -1)::int
from p
;

然后,我们将使用带过滤器选项的聚合函数。

谢谢!这实际上是我在子查询方面已经拥有的。绝对有效。我没有意识到Postgres允许你创建一个索引并将其与一个条件相结合。这真是太棒了。+1…这是一个非常聪明的解决方案,而且
id
value
上的索引将提高性能。2015年对我来说是一个很棒的解决方案!您可以使用
count(value)
而不是
sum(case…
,因为count忽略空值。奇妙的发现。这在内特扎也很有效。这也适用于普雷斯托(或雅典娜)因为它们也不支持用于LAG的IGNORE NULLS子句。您可以使用伪类型
anyelement
而不是
int
来使函数和聚合函数通用。调用它
coalesce\u agg
似乎更直观。
coalesce\u agg
似乎更好。尽管吹毛求疵,
coalesce
更喜欢第一个参数,而这个聚合函数更喜欢最后一个值。带有非聚合窗口函数的过滤器还没有在Postgres中实现。或者,遗憾的是,11。尽管这是最干净的解决方案。
with p (id, value) as (
    values (1, 100),
           (2, null),
           (3, null),
           (4, null),
           (5, null),
           (6, null),
           (7, null),
           (8, 200),
           (9, null))
select *
     , (json_agg(value) filter (where value notnull) over (order by id) ->> -1)::int
from p
;