1

Ich versuche, eine Zwischenspeichertabelle ('neue Daten') mit einer anderen Tabelle ('vorhandene Daten') zu vergleichen, um hinzugefügte/geänderte/entfernte Zeilen und schließlich ein Upsert zu identifizieren. Dies ist eine teure Operation - ein vollständiger Unterschied in einem großen Datensatz. Ich wollte wirklich den EXCEPT Befehl für syntaktische Klarheit verwenden, aber habe ernsthafte Leistungsprobleme damit, und finde einen LEFT JOIN ist viel besser.Redshift EXCEPT viel langsamer als LEFT JOIN

Die beiden Tabellen haben eine ähnliche Anzahl von Zeilen und das gleiche Schema (fast - die 'zweite' Tabelle hat eine zusätzliche created_date Spalte).

Beide teilen distkey(date) und sortkey(date, id1, id2); Ich gebe sogar die Spalten in der 'richtigen' Reihenfolge innerhalb der EXCEPT-Anweisung an, um dem Optimierer zu helfen.

Die Abfragepläne für jede, auf einer Testgröße-Teilmenge der Daten, sind unten.

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) 

Vergleichen mit meinem viel hässlicher 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) 

Die Abfragekosten sind 1000003266822.61 vs 68706795.68. Ich weiß, dass ich keine Queries vergleichen soll, aber es hat sich in den Ausführungszeiten bewährt. Irgendwelche Ideen, warum eine EXCEPT Aussage ist so viel langsamer als eine LEFT JOIN hier?

Antwort

2

Die left join erzeugt einen Stapel von kreuzweise verbundenen Zeilen für jeden (vermutlich geordneten) Schlüsselwert und filtert dann diejenigen heraus, die nicht erwünscht sind über on; es kann auch aufhören, wenn die (vermutlich geordneten) alten Schlüsselwerte die neuen Schlüsselwerte überschreiten, da es keine weiteren Übereinstimmungen geben kann - was auch einige Schlüsse über einige SARG-Smarts einschließt. Die except sortiert alles zuerst. In diesem Fall kostet das Sortieren mehr als das Erzeugen von & Reihen wegwerfen, das Gehen durch die einzelnen Reihen der Schlüssel des rechten Tischs multiplizieren. Natürlich könnte der Optimierer ein outer join Idiom in seine except Planung einschließen - aber es scheint nicht.

In Verbindung stehende: PostgreSQL: NOT IN versus EXCEPT performance difference

Verwandte Themen