訪問を記録するテーブルを考えます
create table visits (
person varchar(10),
ts timestamp,
somevalue varchar(10)
)
このサンプルデータを検討してください(カウンターとして簡略化されたタイムスタンプ)
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
私は、その値が変化する(つまり、次の非null値になる)まで、その人の最後の非null somevalueをすべての将来の訪問に持ち越そうとしています。
期待される結果セットは次のようになります。
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
私の試みは次のようになります:
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
注:(somevalue is null)は、ソートの目的で1または0に評価されるため、パーティション内の最初の非null値を取得できます。
上記は私が求めている結果を私に与えません。
次のクエリは、目的の結果を実現します。
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ステートメントに注意してください-IGNORE_NULLがpostgresウィンドウ関数によってサポートされていた場合、これは必要ありません(@ypercubeᵀᴹで言及)。
問題は、ギャップとアイランドのカテゴリーの問題です。 PostgresがFIRST_VALUE()
のようなウィンドウ関数にIGNORE NULL
をまだ実装していないのは残念です。それ以外の場合は、クエリを変更するだけで簡単です。
これをウィンドウ関数または再帰CTEを使用して解決するには、おそらく多くの方法があります。
それが最も効率的な方法かどうかはわかりませんが、再帰的なCTEが問題を解決します。
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 ;