2

postgresql 9.5 で 'LEFT JOIN LATERAL' 関数を使用するのに問題があります。

私のテーブルには、「ID」、「DATE」、「CODE」の 3 つの列があります。1 人 (ID) は、以下のように複数の行があります。ID数は362、総行数は約250万行です。

ID   /  DATE     / CODE
1    /  20020101 / drugA
1    /  20020102 / drugA
1    /  20020103 / drugB
1    /  20020104 / drugA
1    /  20020105 / drugA
1    /  20020106 / drugB
1    /  20020107 / drugA
2    /  ...      / ...

薬剤Bの初日から最終日までに使用された薬剤Aの情報を要約する必要があります。

上記の場合、ID(1) [20020103 ~ 20020106 の間; 薬物Bの期間]。

1    /  20020104 / drugA
1    /  20020105 / drugA

この仕事を引き受けるために、以下のように 'LEFT LATERAL JOIN' を使用して SQL コードを記述します。

SELECT * FROM (SELECT ID, min(DATE) as start_date, max(DATE) as end_date from MAIN_TABLE WHERE CODE = 'drugA' GROUP BY ID) AA
LEFT JOIN LATERAL (SELECT ID, COUNT(ID) as no_tx, min(DATE) as fday_tx, max(DATE) lday_tx from MAIN_TABLE WHERE CODE = 'drugB' AND DATE > AA.start_date AND DATE < AA.end_date GROUP BY ID) as BB USING(ID);

個人 ID は 362 個しかありませんが、この postgresql コードには約 2 分かかります。

遅すぎる。したがって、サブクエリを使用して別の SQL コードを試しました。

SELECT * FROM (SELECT ID, min(DATE) as start_date, max(DATE) as end_date from MAIN_TABLE WHERE CODE ='drugA' GROUP BY ID) AA
LEFT JOIN (
       SELECT ID, COUNT(ID) as no_tx, min(DATE) as fday_tx, max(DATE) lday_tx FROM (SELECT ID, DATE, CODE FROM MAIN_TABLE) BB
            LEFT JOIN (SELECT ID, min(DATE) as start_date, max(DATE) as end_date from MAIN_TABLE WHERE CODE ='drugA' GROUP BY ID) CC USING (ID)
            WHERE CODE = 'drugB' and DATE > start_date and DATE < end_date GROUP BY ID
            ) DD USING (ID);

このコードは単純ではありませんが、非常に高速です (わずか 1.6 秒しかかかりません)。

2 つのコードの説明を比較すると、2 番目のコードではハッシュ結合が使用されていますが、最初のコードでは使用されていません。

「LEFT LATERAL JOIN」関数を使用して最初のコードをより効率的に改善するためのヒントを得ることができますか?

4

1 に答える 1

2

joinとを使用しないのはなぜgroup byですか?

SELECT AA.ID, COUNT(B.ID) as no_tx, min(B.DATE) as fday_tx, max(B.DATE) as lday_tx,
       AA.start_date, AA.end_date
FROM (SELECT ID, min(DATE) as start_date, max(DATE) as end_date 
      FROM MAIN_TABLE
      WHERE CODE = 'drugA'
      GROUP BY ID
     ) AA LEFT JOIN
     MAIN_TABLE b
     ON b.CODE = 'drugB' AND b.DATE > AA.start_date AND b.DATE < AA.end_date
GROUP BY AA.ID,  AA.start_date, AA.end_date;

または、おそらくより効率的に、ウィンドウ関数:

SELECT ID, SUM(CASE WHEN code = 'drugB' THEN 1 ELSE 0 END) as no_tx,
       MIN(CASE WHEN code = 'drugB' THEN DATE END) as fday_tx,
       MIN(CASE WHEN code = 'drugB' THEN DATE END) as lday_tx,
       start_date, end_date
FROM (SELECT t.*,
             MIN(CASE WHEN code = 'drugA' THEN date END) as start_date,
             MAX(CASE WHEN code = 'drugB' THEN date END) as end_date
      FROM MAIN_TABLE t
     ) t
WHERE code in ('drugA', 'drugB') AND
      date between start_date and end_date
GROUP BY t.id;
于 2016-02-15T15:46:54.570 に答える