Bir bölümdeki ilk null olmayan değeri ileri taşımak için window işlevini kullanma


12

Ziyaretleri kaydeden bir tablo düşünün

create table visits (
  person varchar(10),
  ts timestamp, 
  somevalue varchar(10) 
)

Bu örnek verileri ele alalım (zaman damgası sayaç olarak basitleştirildi)

ts| person    |  somevalue
-------------------------
1 |  bob      |null
2 |  bob      |null
3 |  jim      |null
4 |  bob      |  A
5 |  bob      | null
6 |  bob      |  B
7 |  jim      |  X
8 |  jim      |  Y
9 |  jim      |  null

Bu değer değiştiğinde (yani bir sonraki sıfır olmayan değer) değer gelene kadar kişinin gelecekteki tüm ziyaretlerine son null olmayan bir ortam iletmeye çalışıyorum.

Beklenen sonuç kümesi şöyle görünür:

ts|  person   | somevalue | carry-forward 
-----------------------------------------------
1 |  bob      |null       |   null
2 |  bob      |null       |   null
3 |  jim      |null       |   null
4 |  bob      |  A        |    A
5 |  bob      | null      |    A
6 |  bob      |  B        |    B
7 |  jim      |  X        |    X
8 |  jim      |  Y        |    Y
9 |  jim      |  null     |    Y

Girişimim şöyle:

 select *, 
  first_value(somevalue) over (partition by person order by (somevalue is null), ts rows between UNBOUNDED PRECEDING AND current row  ) as carry_forward

 from visits  
 order by ts

Not: (somevalue null), sıralama amacıyla 1 veya 0 olarak değerlendirilir, böylece bölümdeki ilk null olmayan değeri alabilirim.

Yukarıdakiler bana peşimde olan sonucu vermiyor.


pg_dumpVerileri bir psql çıktısına ve tablo şemasına yapıştırmak yerine test verileriniz için yapıştırabilir misiniz ? pg_dump -t table -d databaseoluşturma ve COPYkomutlara ihtiyacımız var .
Evan Carroll


1
@a_horse_with_no_name bir cevap olmayı hak ediyor.
ypercubeᵀᴹ

Yanıtlar:


13

Aşağıdaki sorgu istenen sonucu elde eder:

select *, first_value(somevalue) over w as carryforward_somevalue
from (
  select *, sum(case when somevalue is null then 0 else 1 end) over (partition by person order by id ) as value_partition
  from test1

) as q
window w as (partition by person, value_partition order by id);

Null case deyimini not edin - IGNORE_NULL postgres pencere işlevleri tarafından destekleniyorsa buna gerek olmazdı (@ ypercubeᵀᴹ tarafından belirtildiği gibi)


5
Also the simplecount(somevalue) over (...)
ypercubeᵀᴹ

5

Sorun, boşluklar ve adalar kategorisindedir. Postgres'in IGNORE NULLpencere işlevlerinde henüz uygulanmadığı üzücü FIRST_VALUE(), aksi takdirde sorgunuzda basit bir değişiklikle önemsiz olurdu.

Bunun, pencere işlevleri veya özyinelemeli CTE'ler kullanılarak çözülmesinin birçok yolu vardır.

En etkili yol olup olmadığından emin değil, özyinelemeli bir CTE sorunu çözüyor:

with recursive 
    cf as
    (
      ( select distinct on (person) 
            v.*, v.somevalue as carry_forward
        from visits as v
        order by person, ts
      ) 
      union all
        select 
            v.*, coalesce(v.somevalue, cf.carry_forward)
        from cf
          join lateral  
            ( select v.*
              from visits as v
              where v.person = cf.person
                and v.ts > cf.ts
              order by ts
              limit 1
            ) as v
            on true
    )
select cf.*
from cf 
order by ts ;

Sorunu gerçekten çözüyor, ancak olması gerekenden daha karmaşık. Aşağıdaki cevabımı gör
maxTrialfire

1
Evet, cevabınız iyi görünüyor!
ypercubeᵀᴹ
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.