Der CTE ist langsamer, da er unverändert (über einen CTE-Scan) ausgeführt werden muss.
TFM (Abschnitt 7.8.2) besagt: Datenmodifizierende Anweisungen in WITH werden ausgeführt genau einmal und immer bis zum Ende, unabhängig davon, ob die primäre Abfrage alle (oder tatsächlich einige) ihrer Ausgabe liest nur so weit durchgeführt, wie die primäre Abfrage ihre Ausgabe erfordert.
Es ist somit eine Optimierungsbarriere; Für den Optimierer ist die Demontage des CTE nicht erlaubt, selbst wenn dies zu einem intelligenteren Plan mit den gleichen Ergebnissen führen würde.
Die CTE-Lösung kann jedoch in eine verbundene Unterabfrage umgestaltet werden (ähnlich der temporären Tabelle in der Frage). In Postgres ist eine verknüpfte Unterabfrage heutzutage normalerweise schneller als die Variante EXISTS().
DELETE FROM customer del
USING ( SELECT id
, row_number() over(partition by uuid order by created_date desc)
as rn
FROM customer
) sub
WHERE sub.id = del.id
AND sub.rn > 1
;
Eine andere Möglichkeit ist die Verwendung einer TEMP VIEW
. Dies ist syntaktisch entspricht der temp table
Fall, aber semantisch Äquivalent zum verbundenen Unterabfrageformular (sie ergeben genau derselbe Abfrageplan, zumindest in diesem Fall). Dies liegt daran, dass der Optimierer von Postgres demontiert die Ansicht und kombiniert sie mit der Hauptabfrage (Pull-up ). Sie könnten eine view
sehen als eine Art Makro in PG.
CREATE TEMP VIEW targets
AS SELECT id
, row_number() over(partition by uuid ORDER BY created_date DESC) AS rn
FROM customer;
EXPLAIN
DELETE FROM customer
WHERE id IN ( SELECT id
FROM targets
WHERE rn > 1
);
[AKTUALISIERT:Ich habe mich geirrt, dass CTEs immer vollständig ausgeführt werden müssen, was nur bei datenmodifizierenden CTEs der Fall ist]