Chegamos ao último post da nossa série essencial de SQL! Passamos pelas siglas do banco, entendemos a estrutura e como criar automações. Mas guardamos para o final o comando que mais exige maturidade, atenção e processos bem definidos por parte de qualquer profissional de dados ou DevOps: o DELETE.
Inserir dados incorretos gera lixo; consultar dados de forma lenta causa lentidão temporária; mas deletar dados errados em produção pode significar a perda definitiva de informações críticas de negócio, horas de downtime para restaurar backups e, no pior dos cenários, prejuízo financeiro.
Neste post, vamos aprender a dominar o comando DELETE no PostgreSQL, entender como utilizá-lo de forma cirúrgica e ver quais as melhores práticas de infraestrutura para mitigar riscos em ambientes vivos.
A Sintaxe Básica (E o perigo que mora nela)
A estrutura do comando DELETE é assustadoramente simples:
DELETE FROM nome_da_tabela WHERE condicao;
Se você esquecer ou errar a cláusula WHERE, o PostgreSQL não vai te perguntar “Tem certeza?”. Ele simplesmente vai limpar a tabela inteira, linha por linha.
Estratégias para um DELETE Cirúrgico e Seguro
Para evitar desastres quando você precisa rodar uma remoção manual direto na CLI de produção, adote estes três hábitos de segurança:
1. Transforme o DELETE em um SELECT primeiro
Antes de apertar o botão de exclusão, certifique-se de que o seu filtro WHERE está selecionando exatamente o que você deseja apagar.
Se você precisa apagar os logs de staging antigos, rode isto primeiro:
SELECT id, nome_app, ambiente FROM deploys
WHERE ambiente = 'staging' AND criado_em < NOW() - INTERVAL '30 days';
Olhe para as linhas retornadas. São elas mesmas? Se sim, mude a primeira linha do comando para DELETE:
DELETE FROM deploys
WHERE ambiente = 'staging' AND criado_em < NOW() - INTERVAL '30 days';
2. Force o uso de Transações (A rede de segurança)
Nunca rode um DELETE solto em produção. Envolva o comando em uma transação (BEGIN). Dessa forma, o banco executa a remoção, mas ela só se torna definitiva quando você digita COMMIT. Se você notar que o terminal reportou que apagou 1.000 linhas quando deveria apagar apenas 1, digite ROLLBACK imediatamente e tudo volta ao normal.
BEGIN;
-- Tentativa de delete
DELETE FROM servidores WHERE status = 'desativado';
-- Se o terminal disser "DELETE 1", está correto:
COMMIT;
-- Se o terminal disser "DELETE 54320", deu ruim:
ROLLBACK;
3. Deletes Massivos? Faça em Lotes (Batches)
Se você precisa apagar 10 milhões de linhas de logs antigos de uma tabela viva, rodar um único DELETE gigante vai bloquear a tabela inteira por minutos, gerando um overhead massivo no WAL (Write-Ahead Logging) e na CPU do Postgres.
A boa prática de engenharia dita que você deve apagar em blocos menores (ex: de 5.000 em 5.000 linhas) usando um script ou um loop simples, dando tempo para o banco “respirar” entre as execuções:
-- Exemplo de deleção limitada por lote
DELETE FROM logs_aplicacao
WHERE id IN (
SELECT id FROM logs_aplicacao
WHERE criado_em < NOW() - INTERVAL '90 days'
LIMIT 5000
);
DELETE vs TRUNCATE (Visão de Infraestrutura)
Se o seu objetivo é limpar uma tabela de testes ou de staging por completo, não use DELETE FROM tabela;. Use o comando DDL TRUNCATE:
TRUNCATE TABLE logs_teste;
- Por que o TRUNCATE é melhor para zerar tabelas? O
DELETEpassa linha por linha, gerando logs de transação e deixando “buracos” no disco (espaço que depois precisa ser limpo pelo processo de VACUUM do Postgres). OTRUNCATEsimplesmente joga fora o arquivo de dados antigo no disco e cria um novo, vazio. Ele é instantâneo e libera o espaço em disco para o sistema operacional imediatamente.
Alternativa de Arquitetura: Soft Delete
Muitas empresas adotam uma política de nunca apagar dados fisicamente. Em vez disso, utilizam o Soft Delete (Exclusão Lógica). Adiciona-se uma coluna chamada deletado_em ou ativo na tabela:
-- Em vez de apagar, apenas mudamos o estado
UPDATE servidores
SET ativo = false, deletado_em = NOW()
WHERE id = 12;
Sua aplicação passa a filtrar apenas os registros onde ativo = true. Se alguém apagar algo por engano, recuperar o dado é tão simples quanto dar um UPDATE ... SET ativo = true.
Conclusão e O Próximo Nível
O comando DELETE fecha oficialmente a nossa trilha essencial de comandos SQL. Passamos por toda a fundação necessária para manipular, estruturar, proteger e otimizar bases de dados relacionais utilizando o PostgreSQL como nosso guia.
Mas o que acontece com o disco rígido e com a performance do banco após apagarmos milhões de registros? Como o Postgres gerencia o espaço que sobrou? Para fechar a seção de banco de dados com chave de ouro e com o olhar de quem cuida da infraestrutura, teremos um conteúdo especial.
No próximo post (um extra da nossa série), vamos abrir o capô do PostgreSQL para entender os comandos de manutenção mais vitais de um DBA e de um time de DevOps: o VACUUM e o ANALYZE. Vamos aprender como evitar o inchaço de tabelas (bloat) e como manter o otimizador de queries inteligente após grandes deleções.
Depois desse último ajuste técnico, estaremos prontos para inaugurar a seção mais aguardada do blog: DevOps Avançado para Bancos de Dados, onde aprenderemos como colocar o PostgreSQL para rodar dentro do Kubernetes (K8s) de forma resiliente, utilizando operadores modernos (como o CloudNativePG) para automatizar alta disponibilidade, failover dinâmico e backups nativos. Prepare seu arquivo YAML e nos vemos lá!
Você já teve que acionar o backup da empresa por causa de um DELETE sem WHERE? Qual estratégia de deleção (Hard Delete ou Soft Delete) seu time usa hoje? Participe nos comentários!