PostgreSQL last_value ignore nulls

女生的网名这么多〃 提交于 2019-12-11 05:18:08

问题


I know this already been asked, but why doesn't the solution below work? I want to fill value with the last non-null value ordered by idx.

What I see:

 idx | coalesce 
-----+----------
   1 |        2
   2 |        4
   3 |         
   4 |         
   5 |       10
(5 rows)

What I want:

 idx | coalesce 
-----+----------
   1 |        2
   2 |        4
   3 |        4 
   4 |        4 
   5 |       10
(5 rows)

Code:

with base as (

    select 1    as idx
         , 2    as value

    union

    select 2    as idx
         , 4    as value

    union

    select 3    as idx
         , null as value

    union

    select 4    as idx
         , null as value

    union

    select 5    as idx
         , 10   as value
)

select idx
     , coalesce(value
              , last_value(value) over (order by case when value is null then -1
                                                 else idx
                                                 end))
from base
order by idx

回答1:


To see why your solution doesn't work, just look at the output if you order by the ordering in your window frame:

with base as (
    select 1    as idx
         , 2    as value
    union
    select 2    as idx
         , 4    as value
    union
    select 3    as idx
         , null as value
    union
    select 4    as idx
         , null as value
    union
    select 5    as idx
         , 10   as value
)
select idx, value from base
order by case when value is null then -1
                                                 else idx
                                                 end;
 idx | value
-----+-------
   3 |
   4 |
   1 |     2
   2 |     4
   5 |    10

The last_value() window function will pick the last value in the current frame. Without changing any of the frame defaults, this will be the current row.




回答2:


What you want is lag(ignore nulls). Here is one way to do what you want, using two window functions. The first defines the grouping for the NULL values and the second assigns the value:

select idx, value, coalesce(value, max(value) over (partition by grp))
from (select b.*, count(value) over (order by idx) as grp
      from base b
     ) b
order by idx;

You can also do this without subqueries by using arrays. Basically, take the last element not counting NULLs:

select idx, value, 
       (array_remove(array_agg(value) over (order by idx), null))[count(value) over (order by idx)]
from base b
order by idx;

Here is a db<>fiddle.




回答3:


Well the last_value here doesn't make sense to me unless you can point out to me. Looking at the example you need the last non value which you can get it by: I am forming a group with the nulls and previous non null value so that I can get the first non value.

with base as (
select 1    as idx , 2    as value   union
select 2    as idx, -14    as value    union
select 3    as idx , null as value   union
select 4    as idx , null as value   union
select 5    as idx , 1   as value
)
Select idx,value,
first_value(value) Over(partition by rn) as new_val
from(
select idx,value
    ,sum(case when value is not null then 1 end) over (order by idx) as rn
  from   base
) t

here is the code

http://sqlfiddle.com/#!15/fcda4/2



来源:https://stackoverflow.com/questions/56728095/postgresql-last-value-ignore-nulls

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!