CodeGym /Cursos /SQL SELF /Erros comuns ao trabalhar com CTE e como evitá-los

Erros comuns ao trabalhar com CTE e como evitá-los

SQL SELF
Nível 28 , Lição 4
Disponível

Chegou a hora de falar do lado sombrio de trabalhar com CTE — os erros clássicos. Até a query mais top pode dar ruim se você usar essas ferramentas poderosas do jeito errado. Mas relaxa, aqui vai um manual pra diagnosticar e evitar essas tretas!

1. Erro: materialização do CTE e suas consequências

Uma das paradas principais do PostgreSQL com CTE é a materialização padrão. Isso quer dizer que o resultado do CTE é processado e guardado temporariamente na memória (ou no disco, se for dado demais). Se tiver muita query ou dado gigante, pode deixar tudo bem mais lento.

Exemplo:

WITH heavy_data AS (
    SELECT * FROM large_table
)
SELECT * FROM heavy_data WHERE column_a > 100;

Parece que o CTE só filtra os dados. Mas na real, heavy_data é todo carregado e materializado primeiro, só depois rola o filtro. Isso pode demorar pra caramba.

Como evitar?

No PostgreSQL a partir da versão 12, dá pra usar CTE como expressão inline (tipo subquery), o que resolve o lance da materialização. Só usar CTE que aparece uma vez e não precisa guardar resultado intermediário.

Exemplo de abordagem otimizada:

WITH inline_data AS MATERIALIZED (
    SELECT * FROM large_table
)
SELECT * FROM inline_data WHERE column_a > 100;

Dica: se quiser que materialize mesmo, coloca MATERIALIZED. Se não — usa NOT MATERIALIZED.

2. Erro: CTE recursivo entrando em loop infinito

CTE recursivo é brabo, mas se usar sem limite de profundidade pode virar loop infinito. Isso não só deixa tudo lento, mas também torra todos os recursos do servidor.

Exemplo:

WITH RECURSIVE endless_loop AS (
    SELECT 1 AS value

    UNION ALL

    SELECT value + 1
    FROM endless_loop
)
SELECT * FROM endless_loop;

Isso aí vai gerar linha sem parar, porque não tem condição pra parar a recursão.

Como evitar?

Coloca uma condição de parada com WHERE. Tipo assim:

WITH RECURSIVE limited_loop AS (
    SELECT 1 AS value

    UNION ALL

    SELECT value + 1
    FROM limited_loop
    WHERE value < 10
)
SELECT * FROM limited_loop;

Dica: se for usar CTE recursivo pra hierarquia gigante, limita a profundidade com a opção max_recursion_depth do PostgreSQL.

3. Erro: uso errado de UNION e UNION ALL

Quando junta a query base e a recursiva no CTE, escolher errado entre UNION e UNION ALL pode dar resultado estranho. Por exemplo, UNION tira linhas duplicadas, o que custa processamento extra.

Exemplo:

WITH RECURSIVE employee_hierarchy AS (
    SELECT employee_id, manager_id
    FROM employees
    WHERE manager_id IS NULL

    UNION  -- Aqui era melhor usar UNION ALL

    SELECT e.employee_id, e.manager_id
    FROM employees e
    JOIN employee_hierarchy eh ON e.manager_id = eh.employee_id
)
SELECT * FROM employee_hierarchy;

No exemplo acima, UNION pode sumir com linhas importantes da hierarquia se elas repetirem. E ainda deixa a query mais lenta!

Como corrigir?

Usa UNION ALL se não precisa tirar duplicata:

UNION ALL

4. Erro: CTE demais numa query só

Pra deixar a query mais organizada, tem gente que mete dezenas de CTE. Isso só deixa o código confuso e sobrecarrega o planner do PostgreSQL.

Exemplo:

WITH cte1 AS (...),
     cte2 AS (...),
     cte3 AS (...),
     ...
     cte20 AS (...)
SELECT ...
FROM cte20;

Parece pesadelo de dev, né?

Como corrigir?

— Quebra a query em várias mais simples. Em vez de um megaquery com um monte de CTE, faz várias queries independentes.

— Outra: pra resultados intermediários que precisa usar várias vezes, salva em tabelas temporárias.

5. Erro: CTE complexo sem índice

Se o CTE mexe com muito dado e você esqueceu de criar índice nas tabelas, as queries vão ficar lentas demais. Índice é tipo turbo no banco de dados.

Exemplo:

WITH filtered_data AS (
    SELECT * FROM large_table WHERE unindexed_column = 'value'
)
SELECT * FROM filtered_data;

Como corrigir?

Antes de usar CTE, garante que as tabelas estão otimizadas:

CREATE INDEX idx_large_table ON large_table(unindexed_column);

6. Erro: tentar usar CTE pra acessar dados várias vezes

O CTE é criado, executado e depois "congelado". Se precisar usar o resultado em vários lugares, os dados não vão ser recalculados — e isso pode dar erro.

Exemplo:

WITH data AS (
    SELECT x, y FROM some_table
)
SELECT x FROM data
WHERE y > 10;
-- Se precisar recalcular data de novo, não vai rolar.

Como corrigir?

Se precisa de resultado dinâmico ou recalcular, talvez CTE não seja a melhor. Usa subquery.

7. Erro: falta de comentários

CTE pode ser animal, mas quem curte uma query SQL cabulosa que ninguém entende depois de duas semanas, nem você?

Exemplo:

WITH data_filtered AS (
    SELECT *
    FROM large_table
    WHERE some_column > 100
)
SELECT * FROM data_filtered;

Daqui um mês ninguém lembra porque filtrou esses dados!

Então comenta as queries, principalmente se usar CTE complexo ou recursivo:

WITH data_filtered AS (
    -- Filtro dos dados onde some_column > 100
    SELECT *
    FROM large_table
    WHERE some_column > 100
)
SELECT * FROM data_filtered;

8. Erro: usar CTE demais no lugar de tabela temporária

Às vezes tabela temporária é bem melhor. Tipo quando o resultado vai ser usado várias vezes em queries diferentes ou quando tem dado demais.

Exemplo:

WITH temp_data AS (
    SELECT * FROM large_table
)
SELECT * FROM temp_data WHERE column_a > 100;
SELECT * FROM temp_data WHERE column_b < 50;

Essa query com CTE vai rodar duas vezes, mesmo que os dados não mudem!

Como corrigir?

Cria uma tabela temporária se for usar os dados várias vezes:

CREATE TEMP TABLE temp_table AS
SELECT * FROM large_table;

SELECT * FROM temp_table WHERE column_a > 100;
SELECT * FROM temp_table WHERE column_b < 50;

Dica final

Como qualquer função poderosa, com CTE é importante sacar que nem sempre é a melhor ferramenta pra tudo. Pensa bem no porquê e como vai usar. O lance de "quanto mais CTE melhor" pode ferrar a performance e a leitura do código. E claro, sempre faz teste de performance e otimiza as queries.

1
Pesquisa/teste
Otimização de queries, nível 28, lição 4
Indisponível
Otimização de queries
Otimização de queries
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION