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.
GO TO FULL VERSION