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

xv8emn3q  于 6个月前  发布在  PostgreSQL
关注(0)|答案(4)|浏览(70)

我想在Posterre中执行向下填充操作。
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星星|
| 巧克力|dairy milk|
| 巧克力|perk|
| 巧克力|Eclair|
| 饼干|Britannia|
| 饼干|好日子|
| 饼干|提振|
| 洗发水|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

vof42yt1

vof42yt11#

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;

xcitsw88

xcitsw882#

我认为使用CTE没有什么错(见JHH的回答),我更喜欢这样。
Postgres数据库不提供SQLServer数据库的IGNORE NULLS概念,所以我想你应该停止认为你会得到一个几乎像MS SQL一样的Postgres数据库查询。
无论如何,如果你不想使用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<>fiddle
如果你热衷于定义和使用自己的函数,并且你会经常使用它,你可以这样做。
否则,就用CTE,那很好。没有理由不用CTE的。
请始终注意,在使用自己的函数时,您有性能不佳的风险,因此您应该检查此查询是否太慢。

uyto3xhc

uyto3xhc3#

恐怕这在Postgres中没有实现(至少在Postgres 15之前)。manual about window functions
SQL标准为leadlagfirst_valuelast_valuenth_value定义了RESPECT NULLSIGNORE NULLS选项。这在PostgreSQL中没有实现:行为总是与标准的默认值相同,即RESPECT NULLS
因此,您必须使用JHH建议的CTE或子查询的解决方案,或者滚动自己的窗口函数(这将相对较慢)。
参见(dba.SE上类似问题的答案):

ghhkc1vu

ghhkc1vu4#

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

字符串

相关问题