รวมสองตารางเหตุการณ์ไว้ในไทม์ไลน์เดียว


12

รับสองตาราง:

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

ฉันต้องการที่จะเขียนแบบสอบถามว่าค่าตอบแทนts, fooและbarที่แสดงถึงมุมมองแบบครบวงจรของค่าล่าสุด กล่าวอีกนัยหนึ่งถ้าfooมี:

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

และbarบรรจุ:

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

ฉันต้องการคิวรีที่ส่งคืน:

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

หากทั้งสองตารางมีเหตุการณ์ในเวลาเดียวกันลำดับไม่สำคัญ

ฉันสามารถสร้างโครงสร้างที่จำเป็นโดยใช้ค่ายูเนี่ยนทั้งหมดและค่าดัมมี่:

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

ซึ่งจะให้ไทม์ไลน์แบบเส้นตรงของค่าใหม่ แต่ฉันไม่สามารถหาวิธีเติมค่าว่างตามแถวก่อนหน้าได้ ฉันได้ลองใช้lagฟังก์ชั่นหน้าต่างแล้ว แต่ AFAICT มันจะดูที่แถวก่อนหน้าเท่านั้นไม่ย้อนกลับแบบวนซ้ำ ฉันดู CTE แบบเรียกซ้ำ แต่ฉันไม่แน่ใจว่าจะตั้งเงื่อนไขเริ่มต้นและสิ้นสุดได้อย่างไร


คุณค่าในการขึ้นfooและลงbarอย่างเข้มงวดเมื่อเวลาผ่านไปหรือเป็นกรณีทดสอบที่ทำให้เข้าใจผิดในแง่นี้หรือไม่?
Erwin Brandstetter

2
เพื่อช่วยผู้อื่นให้ยุ่งยากsqlfiddle.com/#!15/511414
Craig Ringer

1
แทนที่จะเปลี่ยนลักษณะของคำถามหลังจากคำตอบที่ได้รับโปรดถามคำถามใหม่ คุณสามารถลิงก์ไปยังเอกสารนี้เพื่ออ้างอิงได้เสมอ (คุณสามารถให้คำตอบของคุณเองถ้าคุณมี) เวอร์ชันดั้งเดิมควรเป็นที่สนใจของคนทั่วไป อย่าแพ็คให้มากในคำถามเดียว
Erwin Brandstetter

ขออภัยที่เกินพิกัด ผมเคยเอาออกติดตามขึ้นและเพิ่มเป็นคำถามใหม่
Christopher Currie

คำตอบ:


7

ใช้ a FULL [OUTER] JOIN, รวมกับฟังก์ชั่นหน้าต่างสองรอบ:

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()ไม่นับค่า NULL มันจะเพิ่มเฉพาะค่าที่ไม่ใช่ค่าว่างเท่านั้นดังนั้นจึงสร้างกลุ่มที่จะแบ่งปันค่าเดียวกัน ในด้านนอกSELECT, min()(หรือmax()) เช่นเดียวกันละเว้นค่าเป็นศูนย์จึงเลือกหนึ่งค่า null ไม่ใช่ต่อกลุ่ม voila

FULL JOINกรณีที่เกี่ยวข้อง:

เป็นหนึ่งในกรณีที่วิธีการแก้ปัญหาขั้นตอนอาจจะเร็วขึ้นเพราะสามารถทำงานได้ในการสแกนเพียงครั้งเดียว เช่นนี้ฟังก์ชั่น plpgsql :

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;

โทร:

SELECT * FROM f_merge_foobar();

db <> ซอที่นี่แสดงให้เห็นถึงทั้งสอง

คำตอบที่เกี่ยวข้องอธิบาย#variable_conflict use_column:


ปัญหาที่น่าสนใจใช่ไหม ฉันคิดว่าวิธีแก้ปัญหาที่มีประสิทธิภาพน่าจะต้องมีการสร้างcoalesceฟังก์ชั่นคล้ายหน้าต่าง
Craig Ringer

@ CraigRinger: แน่นอน ฉันคิดว่าตัวเองต้องการที่จะสงสัยสงสัยว่า .. ว่าอย่างนี้ควรจะเป็นไปได้โดยไม่ต้องแบบสอบถามย่อย แต่ฉันล้มเหลวในการหาวิธี เป็นหนึ่งในกรณีที่ฟังก์ชัน plpgsql จะเร็วขึ้นเนื่องจากสามารถสแกนแต่ละตารางได้หนึ่งครั้ง
Erwin Brandstetter

@ คริสโตเฟอร์: ฉันสนใจในประสิทธิภาพของตัวแปรแต่ละตัวในการตั้งค่าของคุณ EXPLAIN ANALYZEที่ดีที่สุดของ 5 ...
Erwin Brandstetter

2
น่าเสียดายที่ Postgres ยังไม่ได้ติดตั้งIGNORE NULLS(ตามที่ Oracle มี: sqlfiddle.com/#!4/fab35/1 )
ypercubeᵀᴹ

1
@ypercube: ใช่ Oracle อย่างง่ายไม่เก็บค่า NULL เลยและไม่สามารถบอกความแตกต่างระหว่าง''และ NULL ได้
Erwin Brandstetter
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.