PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Excluir usando CTE mais lento do que usar tabela temporária no Postgres


O CTE é mais lento porque deve ser executado inalterado (por meio de uma varredura CTE).

A TFM (seção 7.8.2) declara: As instruções de modificação de dados em WITH são executadas exatamente uma vez, e sempre até a conclusão, independentemente de a consulta primária ler toda (ou alguma) sua saída. Observe que isso é diferente da regra para SELECT em WITH:conforme declarado na seção anterior, a execução de um SELECT é transportado apenas até onde a consulta primária exige sua saída.

É, portanto, uma barreira de otimização; para o otimizador, não é permitido desmontar o CTE, mesmo que resulte em um plano mais inteligente com os mesmos resultados.

A solução CTE pode ser refatorada em uma subconsulta unida (semelhante à tabela temporária na pergunta). No postgres, uma subconsulta unida geralmente é mais rápida que a variante EXISTS(), hoje em dia.
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
        ;

Outra maneira é usar um TEMP VIEW . Isso é sintaticamente equivalente à temp table caso, mas semanticamente equivalente ao formulário de subconsulta unida (eles produzem exatamente o mesmo plano de consulta, pelo menos neste caso). Isso ocorre porque o otimizador do Postgres desmonta a visualização e a combina com a consulta principal (pull-up ). Você pode ver uma view como uma espécie de macro no 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
        );

[ATUALIZADO:Eu estava errado sobre os CTEs precisarem ser sempre executados até a conclusão, o que é apenas o caso dos CTEs de modificação de dados]