⌘K

14 – SQL no PostgreSQL: Como Usar o Comando DELETE em Produção com Segurança Máxima

Last updated

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 DELETE passa 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). O TRUNCATE simplesmente 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!

Still stuck? How can we help? Get Help