Kết hợp hai bảng sự kiện thành một dòng thời gian duy nhất


12

Cho hai bảng:

CREATE TABLE foo (ts timestamp, foo text);
CREATE TABLE bar (ts timestamp, bar text);

Tôi muốn viết một truy vấn mà trở về giá trị cho ts, foobarđại diện cho một cái nhìn thống nhất của các giá trị gần đây nhất. Nói cách khác, nếu foocó:

ts | foo
--------
1  | A
7  | B

barchứa:

ts | bar
--------
3  | C
5  | D
9  | E

Tôi muốn một truy vấn trả về:

ts | foo | bar
--------------
1  | A   | null
3  | A   | C
5  | A   | D
7  | B   | D
9  | B   | E

Nếu cả hai bảng có một sự kiện cùng một lúc, thứ tự không thành vấn đề.

Tôi đã có thể tạo cấu trúc cần thiết bằng cách sử dụng các giá trị hợp nhất và giả:

SELECT ts, foo, null as bar FROM foo
UNION ALL SELECT ts, null as foo, bar FROM bar

sẽ cung cấp cho tôi dòng thời gian tuyến tính của các giá trị mới, nhưng tôi không thể tìm ra cách điền các giá trị null dựa trên các hàng trước đó. Tôi đã thử lagchức năng cửa sổ, nhưng AFAICT nó sẽ chỉ nhìn vào hàng trước, không được đệ quy ngược. Tôi đã xem xét các CTE đệ quy, nhưng tôi không chắc chắn làm thế nào để thiết lập các điều kiện bắt đầu và chấm dứt.


Là các giá trị trong foobartăng dần theo thời gian hoặc là trường hợp thử nghiệm sai lệch về mặt này?
Erwin Brandstetter

2
Để cứu những người khác gặp rắc rối, sqlfiddle.com/#!15/511414
Craig Ringer

1
Thay vì thay đổi bản chất của câu hỏi sau khi đã có câu trả lời, vui lòng đặt câu hỏi mới . Bạn luôn có thể liên kết đến cái này để tham khảo. (Bạn thậm chí có thể cung cấp câu trả lời của riêng mình nếu bạn có.) Phiên bản gốc sẽ thú vị với công chúng. Chúng ta đừng gói ghém nhiều trong một câu hỏi.
Erwin Brandstetter

Xin lỗi vì sự quá tải. Tôi đã xóa theo dõi và thêm nó dưới dạng một câu hỏi mới .
Christopher Currie

Câu trả lời:


7

Sử dụng một FULL [OUTER] JOIN, kết hợp với hai vòng chức năng cửa sổ :

SELECT ts
     , min(foo) OVER (PARTITION BY foo_grp) AS foo
     , min(bar) OVER (PARTITION BY bar_grp) AS bar
FROM (
   SELECT ts, f.foo, b.bar
        , count(f.foo) OVER (ORDER BY ts) AS foo_grp
        , count(b.bar) OVER (ORDER BY ts) AS bar_grp
   FROM   foo f
   FULL   JOIN bar b USING (ts)
   ) sub;

count()không tính các giá trị NULL, nó chỉ tăng một cách thuận tiện với mọi giá trị khác không, do đó hình thành các nhóm sẽ chia sẻ cùng một giá trị. Ở bên ngoài SELECT, min()(hoặc max()) tương tự bỏ qua các giá trị NULL, do đó chọn một giá trị khác không cho mỗi nhóm. Voilá.

FULL JOINTrường hợp liên quan :

Đó là một trong những trường hợp mà một giải pháp thủ tục có thể nhanh hơn, vì nó có thể hoàn thành công việc trong một lần quét. Giống như chức năng plpgsql này :

CREATE OR REPLACE FUNCTION f_merge_foobar()
  RETURNS TABLE(ts int, foo text, bar text) AS
$func$
#variable_conflict use_column
DECLARE
   last_foo text;
   last_bar text;
BEGIN
   FOR ts, foo, bar IN
      SELECT ts, f.foo, b.bar
      FROM   foo f
      FULL   JOIN bar b USING (ts)
      ORDER  BY 1
   LOOP
      IF foo IS NULL THEN foo := last_foo;
      ELSE                last_foo := foo;
      END IF;

      IF bar IS NULL THEN bar := last_bar;
      ELSE                last_bar := bar;
      END IF;

      RETURN NEXT;
   END LOOP;
END
$func$ LANGUAGE plpgsql;

Gọi:

SELECT * FROM f_merge_foobar();

db <> fiddle ở đây , chứng minh cả hai.

Câu trả lời liên quan giải thích #variable_conflict use_column:


Vấn đề thú vị không phải là nó. Tôi nghĩ rằng một giải pháp hiệu quả có thể yêu cầu tạo ra một coalescechức năng cửa sổ giống như.
Craig Ringer

@CraigRinger: Thật vậy. Tôi thấy mình mong muốn, tự hỏi, suy nghĩ .. rằng điều này bằng cách nào đó nên có thể mà không cần truy vấn, nhưng tôi đã thất bại trong việc tìm cách. Đây là một trong những trường hợp mà hàm plpgsql sẽ nhanh hơn vì nó có thể quét từng bảng một lần.
Erwin Brandstetter

@Christopher: Tôi sẽ quan tâm đến hiệu suất của từng biến thể trong thiết lập của bạn. EXPLAIN ANALYZE, tốt nhất trong 5 ...?
Erwin Brandstetter

2
Đáng tiếc là Postgres chưa triển khai IGNORE NULLS(như Oracle có: sqlfiddle.com/#!4/fab35/1 ).
ypercubeᵀᴹ

1
@ypercube: Có, Oracle đơn giản không lưu trữ các giá trị NULL và do đó không thể cho biết sự khác biệt giữa ''và NULL.
Erwin Brandstetter
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.