如何使用first_value()窗口函数获取最后一个非空值?

问题描述 投票:0回答:4

我想在Postgres中执行填充操作。

DDL:

create table brands (
id int,
category varchar(20),
brand_name varchar(20)
);

insert into brands values
 (1,'chocolates','5-star')
,(2,null,'dairy milk')
,(3,null,'perk')
,(4,null,'eclair')
,(5,'Biscuits','britannia')
,(6,null,'good day')
,(7,null,'boost')
,(8,'shampoo','h&s')
,(9,null,'dove')
;

预期输出是:

类别 品牌名称
巧克力 5 星
巧克力 牛奶
巧克力 福利
巧克力 泡芙
饼干 不列颠尼亚
饼干 美好的一天
饼干 提升
洗发水 h&s
洗发水 鸽子

我尝试使用以下脚本,但它似乎不起作用。

select id,
      first_value(category)
      over(order by case when category is not null then id end desc nulls last) as category,
      brand_name
from brands

有人可以建议修复吗?
在 MS SQL 中,以下代码片段似乎工作正常:

select id,
       first_value (category) IGNORE NULLS
       over(order by id desc
        rows between current row and unbounded following) as category,
       brand_name
FROM brands
ORDER BY id
sql postgresql window-functions
4个回答
5
投票
with cte as (
select id,
       category,
       count(category) over (order by id) as category_id,
       brand_name
  from brands)
select id,
       first_value(category) over (partition by category_id order by id) as category,
       brand_name
  from cte;

更新:每个请求添加了没有 CTE 的查询:

select id,
       (array_agg(category) over (order by id))[max(case when category is null then 0 else id end) over (order by id)] as category,
       brand_name
  from brands;

2
投票

我认为使用

CTE
没有什么问题(参见JHH的答案),我会更喜欢

Postgres DB 不提供 SQLServer DB 的

IGNORE NULLS
概念,所以我想您应该停止认为您会像 MS SQL 中那样获得几乎相同的 Postgres DB 查询。

无论如何,如果您不想使用 CTE 或复杂的子查询,您可以定义自己的函数和聚合并运行它。

函数创建:

-- CREATE your function
CREATE FUNCTION yourFunction(STATE anyelement, VALUE anyelement)
    RETURNS anyelement
    IMMUTABLE PARALLEL safe
AS
$$
SELECT COALESCE(VALUE, STATE); -- Replace NULL values here
$$ LANGUAGE SQL;

使用函数创建聚合:

-- CREATE your aggregate
CREATE AGGREGATE yourAggregate(ANYELEMENT) (
    sfunc = yourFunction, -- Call your function here
    stype = ANYELEMENT
);

您使用此聚合的查询:

SELECT id, 
  yourAggregate(category) -- Call your aggregate here
  OVER (ORDER BY id, category), 
  brand_name
FROM brands
ORDER BY id;

当然,您应该重命名函数和聚合,并使用更有意义的名称。

这将产生与 CTE 版本相同的结果。

尝试一下:db<>小提琴

如果您热衷于定义和使用自己的函数并且您会经常使用它,那么您可以这样做。

不然的话,用CTE就可以了。没有理由不使用 CTE。

请始终注意,在使用自己的函数时,您面临性能不佳的风险,因此您应该检查此查询是否太慢。


1
投票

恐怕这在 Postgres 中还没有实现(至少在 Postgres 15 之前)。 关于窗口函数的手册

SQL 标准定义了

RESPECT NULLS

IGNORE NULLS
 选项
对于 
lead
lag
first_value
last_value
nth_value
。这
未在 PostgreSQL 中实现:行为始终与
标准的默认值,即 
RESPECT NULLS

因此,您必须使用 CTE 或子查询(如 JHH 建议的)的解决方法,或者滚动您自己的窗口函数(这会相对较慢)。

参见(dba.SE 上类似问题的回答):

  • 使用 Postgres 继承长序列的缺失值

-2
投票
select *, max(category) over (partition by r) from( select *,sum(case when category is not null then 1 end) over (order by rn) r from (select *,row_number() over() as rn from brands) l )p
    
© www.soinside.com 2019 - 2024. All rights reserved.