Le CTE est plus lent car il doit être exécuté tel quel (via un scan CTE).
C'est donc une barrière d'optimisation; pour l'optimiseur, le démantèlement du CTE n'est pas autorisé, même si cela se traduirait par un plan plus intelligent avec les mêmes résultats.
La solution CTE peut cependant être refactorisée en une sous-requête jointe (similaire à la table temporaire de la question). Dans postgres, une sous-requête jointe est généralement plus rapide que la variante EXISTS(), de nos jours.
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
;
Une autre méthode consiste à utiliser une TEMP VIEW
. C'est syntaxiquement équivalent à la temp table
cas, mais sémantiquement équivalent à la forme de sous-requête jointe (ils donnent exactement le même plan de requête, du moins dans ce cas). C'est parce que l'optimiseur de Postgres démonte la vue et la combine avec la requête principale (pull-up ). Vous pourriez voir une view
comme une sorte de macro dans 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
);
[MISE À JOUR :Je me suis trompé sur le fait que les CTE doivent toujours être exécutés jusqu'à leur achèvement, ce qui n'est le cas que pour les CTE qui modifient les données]