Il CTE è più lento perché deve essere eseguito inalterato (tramite una scansione CTE).
Si tratta quindi di una barriera di ottimizzazione; per l'ottimizzatore, lo smantellamento del CTE non è consentito, anche se si tradurrebbe in un piano più intelligente con gli stessi risultati.
Tuttavia, la soluzione CTE può essere rifattorizzato in una sottoquery unita (simile alla tabella temporanea nella domanda). In postgres, una sottoquery unita è solitamente più veloce della variante EXISTS(), al giorno d'oggi.
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
;
Un altro modo è usare un TEMP VIEW
. Questo è sintatticamente equivalente alla temp table
caso, ma semanticamente equivalente al modulo di sottoquery unito (restituiscono esattamente lo stesso piano di interrogazione, almeno in questo caso). Questo perché l'ottimizzatore di Postgres smantella la vista e la combina con la query principale (pull-up ). Potresti vedere una view
come una specie di macro 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
);
[AGGIORNATO:mi sbagliavo sul fatto che i CTE debbano essere sempre eseguiti fino al completamento, che è solo il caso dei CTE che modificano i dati]