두 개의 이벤트 테이블을 단일 타임 라인으로 결합


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

두 테이블에 동시에 이벤트가 있으면 순서는 중요하지 않습니다.

나는 union all과 dummy 값을 사용하여 필요한 구조를 만들 수있었습니다.

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

새로운 값의 선형 타임 라인을 제공하지만 이전 행을 기반으로 null 값을 채우는 방법을 해결할 수는 없습니다. lag창 기능을 시도 했지만 AFAICT는 이전 행만보고 재귀 적으로 뒤로 보지 않습니다. 재귀 CTE를 살펴 봤지만 시작 및 종료 조건을 설정하는 방법을 잘 모르겠습니다.


의 값은 다음 foobar엄격하게 시간이 지남에 따라 상승 또는 테스트 케이스는이 점에 오해의 소지가있다?
Erwin Brandstetter


1
답변을받은 후 질문의 성격을 바꾸지 말고 새로운 질문을하십시오 . 언제든지 참조 용으로 연결할 수 있습니다. (있는 경우 자신의 답변을 제공 할 수도 있습니다.) 원본 버전은 일반 대중에게 흥미로울 것입니다. 한 번의 질문으로 많은 내용을 다루지 마십시오.
Erwin Brandstetter

과부하로 죄송합니다. 후속 조치를 삭제하고 새로운 질문으로 추가했습니다 .
크리스토퍼 커리

답변:


7

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 값을 계산하지 않기 때문에 널이 아닌 모든 값으로 만 편리하게 증가하므로 동일한 값을 공유하는 그룹을 형성합니다. outer SELECT에서 min()(또는 max())도 마찬가지로 NULL 값을 무시하므로 그룹당 하나의 null이 아닌 값을 선택합니다. 보일라

관련 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 <> fiddle here , 둘 다를 보여줍니다.

관련 답변 #variable_conflict use_column:


재미있는 문제가 아닙니다. 효율적인 솔루션에는 아마도 coalesce비슷한 창 함수를 만들어야한다고 생각합니다 .
Craig Ringer

@CraigRinger : 그렇습니다. 나는 나 자신이 희망, 궁금, 생각 .. 이것은 어떻게 든 하위 쿼리없이 가능해야한다는 것을 알지만 방법을 찾지 못했습니다. 각 테이블을 한 번 스캔 할 수 있기 때문에 plpgsql 함수가 더 빠른 경우 중 하나입니다.
Erwin Brandstetter

@Christopher : 귀하의 설정에서 각 변형의 성능에 관심이 있습니다. EXPLAIN ANALYZE, 5의 최고 ...?
Erwin Brandstetter

2
Postgres가 아직 구현하지 않은 동정심IGNORE NULLS (Oracle의 sqlfiddle.com/#!4/fab35/1 )
ypercubeᵀᴹ

1
@ypercube : 예, Oracle simple은 NULL 값을 전혀 저장하지 않으므로 NULL ''과 NULL 의 차이를 알 수 없습니다 .
Erwin Brandstetter
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.