Sử dụng chức năng cửa sổ để chuyển tiếp giá trị không null đầu tiên trong phân vùng


11

Xem xét một bảng ghi lại các lượt truy cập

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

Xem xét dữ liệu ví dụ này (dấu thời gian được đơn giản hóa như bộ đếm)

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

Tôi đang cố gắng chuyển tiếp một ngày nào đó không có giá trị cuối cùng của người đó cho tất cả các lần truy cập trong tương lai của anh ấy cho đến khi giá trị đó thay đổi (tức là trở thành giá trị không null tiếp theo).

Tập kết quả dự kiến ​​trông như thế này:

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

Nỗ lực của tôi trông như thế nà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

Lưu ý: (somevalue là null) ước tính thành 1 hoặc 0 cho mục đích sắp xếp để tôi có thể nhận giá trị không null đầu tiên trong phân vùng.

Ở trên không cho tôi kết quả tôi sau.


Bạn có thể chỉ dán pg_dumpdữ liệu thử nghiệm của mình thay vì dán dữ liệu vào đầu ra psql và lược đồ cho bảng không? pg_dump -t table -d databasechúng ta cần tạo và ra COPYlệnh.
Evan Carroll


1
@a_horse_with_no_name xứng đáng là một câu trả lời.
ypercubeᵀᴹ

Câu trả lời:


11

Truy vấn sau đây đạt được kết quả mong muốn:

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);

Lưu ý câu lệnh null - nếu IGNORE_NULL được hỗ trợ bởi các chức năng cửa sổ postgres thì điều này sẽ không cần thiết (như được đề cập bởi @ ypercubeᵀᴹ)


5
Cũng đơn giảncount(somevalue) over (...)
ypercubeᵀᴹ

4

Vấn đề là trong phạm trù các vấn đề. Thật đáng tiếc khi Postgres chưa thực hiện IGNORE NULLtrong các chức năng của cửa sổ FIRST_VALUE(), nếu không, nó sẽ là tầm thường, với một thay đổi đơn giản trong truy vấn của bạn.

Có lẽ có nhiều cách để giải quyết vấn đề này bằng cách sử dụng các hàm cửa sổ hoặc CTE đệ quy.

Không chắc chắn nếu đó là cách hiệu quả nhất nhưng CTE đệ quy sẽ giải quyết vấn đề:

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 ;

Nó thực sự giải quyết vấn đề tuy nhiên nó phức tạp hơn mức cần thiết. Xem câu trả lời của tôi dưới đây
maxTrialfire

1
Vâng, câu trả lời của bạn có vẻ tốt!
ypercubeᵀᴹ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.