1

私はステージングテーブル( '新しいデータ')を別のテーブル( '既存のデータ')と比較して、追加/変更/削除された行、そして最終的にアップサルトを識別しようとしています。これは高価な操作です - 大きなデータセットでは完全な違いです。構文上の明快さのためにEXCEPTコマンドを使用したかったのですが、重大なパフォーマンス上の問題があり、LEFT JOINがはるかに優れています。Redshift LEFT JOINよりずっと遅い

2つのテーブルには、同じ数の行と同じスキーマがあります(ほぼ「2番目の」テーブルにはさらにcreated_dateの列があります)。

これらは共にdistkey(date)sortkey(date, id1, id2)です。オプティマイザを助けるために、EXCEPTステートメント内の列を「正しい」順序で指定しています。

データのテストサイズのサブセットでのそれぞれのクエリプランは次のとおりです。

explain 
select date, id1, id2, id3, value, attr1, attr2, attr3 from new_data 
except select date, id1, id2, id3, value, attr1, attr2, attr3 from existing_data; 

XN SetOp Except (cost=1000002817944.78..1000003266822.61 rows=1995013 width=1637) 
    -> XN Sort (cost=1000002817944.78..1000002867820.09 rows=19950126 width=1637) 
     Sort Key: date, id1, id2, id3, value, attr1, attr2, attr3 
     -> XN Append (cost=0.00..399002.52 rows=19950126 width=1637) 
       -> XN Subquery Scan "*SELECT* 1" (cost=0.00..199501.26 rows=9975063 width=1637) 
        -> XN Seq Scan on new_data (cost=0.00..99750.63 rows=9975063 width=1637) 
       -> XN Subquery Scan "*SELECT* 2" (cost=0.00..199501.26 rows=9975063 width=1636) 
        -> XN Seq Scan on existing_data (cost=0.00..99750.63 rows=9975063 width=1636) 

は私の非常に醜い LEFT JOIN

explain 
select t1.* from new_data t1 
left outer join existing_data t2 on  
    t1.date = t2.date 
    and t1.id1 = t2.id1 
    and coalesce(t1.id2, -1) = coalesce(t2.id2, -1) 
    and coalesce(t1.id3, -1) = coalesce(t2.id3, -1) 
    and coalesce(t1.value, -1) = coalesce(t2.value, -1) 
    and coalesce(t1.attr1, '') = coalesce(t2.attr1, '') 
    and coalesce(t1.attr2, '') = coalesce(t2.attr2, '') 
    and coalesce(t1.attr3, '') = coalesce(t2.attr3, '') 
where t2.id1 is null; 

XN Merge Left Join DS_DIST_NONE (cost=0.00..68706795.68 rows=9975063 width=1637) 
    Merge Cond: (("outer".date = "inner".date) AND (("outer".id1)::bigint = "inner".id1)) 
    Join Filter: (((COALESCE("outer".id2, -1))::bigint = COALESCE("inner".id2, -1::bigint)) AND ((COALESCE("outer".id3, -1))::bigint = COALESCE("inner".id3, -1::bigint)) AND ((COALESCE("outer".value, -1::numeric))::double precision = COALESCE("inner".value, -1::double precision)) AND ((COALESCE("outer".attr1, ''::character varying))::text = (COALESCE("inner".attr1, ''::character varying))::text) AND ((COALESCE("outer".attr2, ''::character varying))::text = (COALESCE("inner".attr2, ''::character varying))::text) AND ((COALESCE("outer".attr3, ''::character varying))::text = (COALESCE("inner".attr3, ''::character varying))::text)) 
    Filter: ("inner".id1 IS NULL) 
    -> XN Seq Scan on new_data t1 (cost=0.00..99750.63 rows=9975063 width=1637) 
    -> XN Seq Scan on existing_data t2 (cost=0.00..99750.63 rows=9975063 width=1636) 

クエリコストと比較し68706795.681000003266822.61です。私はクエリ間で比較するつもりはないが、実行時間で証明されていることは分かっている。 EXCEPTのステートメントがLEFT JOINよりもずっと遅い理由はありますか?

答えて

2

left joinは、(おそらく順序付けされた)キー値ごとにクロス結合された行のパイルを生成してから、望ましくないものをフィルタリングしてonとします。それ以上の一致がないので(おそらく注文された)古いキー値が新しいキー値を超えたときにも停止することができます。これには、SARGスマートを使用して一部の推論も含まれます。 exceptはすべてを最初にソートしています。この場合、並べ替えのコストは&よりも多くなり、右手のテーブルの各キーの行を複数回歩くことになります。もちろん、オプティマイザには、except計画でouter joinイディオムが含まれていますが、明らかにそうではありません。

関連:PostgreSQL: NOT IN versus EXCEPT performance difference