# DELETE sem WHERE no Oracle: como recuperar seus dados

Você rodou o comando. A sessão travou por um segundo. E aí veio a mensagem:

```
15482 rows deleted.
```

Você queria apagar três.

Se isso já aconteceu com você, sabe que o pior não é o erro. É o silêncio dos dois segundos seguintes, enquanto o cérebro processa o tamanho do estrago.

Tudo que está aqui foi executado numa VM de laboratório com Oracle 19c antes de virar artigo. As saídas que você vê são as que apareceram na tela.

## Recuperar uma tabela apagada é mais comum que restaurar backup

A maior responsabilidade de um DBA é garantir a integridade do banco e ser capaz de recuperar o ambiente do jeito mais rápido possível, com a menor perda de dados possível, dentro dos recursos que existem. Quando se fala em recuperar, a primeira coisa que vem à cabeça de quase todo mundo, dentro das empresas e principalmente de quem está começando, é voltar o backup.

Agora imagine o cenário. Meio do expediente, banco crítico, milhares de pessoas acessando online, e alguém apaga ou altera dados de uma tabela importante. Voltar o backup inteiro para resolver isso significa derrubar o sistema, restaurar tudo, e ainda assim perder o que foi digitado depois do último ponto de recuperação. Usando RMAN ou Data Pump, esse trabalho leva horas, às vezes dias. Para uma aplicação crítica, é impensável.

Ao longo da minha carreira eu lidei muito mais com situações desse tipo do que com crash de servidor inteiro. Perdi a conta de quantas vezes fui acionado porque alguém alterou ou excluiu dados em produção. Os motivos variam e são sempre os mesmos: a pessoa achava que estava conectada em desenvolvimento, esqueceu o WHERE, ou selecionou só o bloco do UPDATE no editor e deixou o WHERE de fora da seleção. Essa última é traiçoeira, porque o comando parece certo na tela.

A causa muda e o resultado é sempre o mesmo: dados que sumiram com o sistema no ar e alguém esperando resposta.

O Flashback, que a Oracle introduziu no 10g, existe exatamente para esse cenário. Ele recupera o que foi perdido em minutos, com o banco funcionando, sem derrubar ninguém. É a ferramenta certa para o incidente mais frequente do dia a dia.

## Antes de recuperar os dados, pare de mexer no banco

Não feche a sessão, não dê COMMIT e não saia rodando comando aleatório para "testar". Cada segundo que passa e cada transação nova no banco reduzem a sua chance de recuperar os dados, porque o undo é um espaço finito e circular. Enquanto você hesita, outras sessões estão gerando undo e sobrescrevendo exatamente o que você precisa.

## Laboratório para reproduzir o delete sem where

Se quiser reproduzir cada passo, crie a tabela abaixo num ambiente de teste. Todo o resto do artigo roda em cima dela. O script completo do laboratório, com os blocos na ordem e os comentários de cada etapa, está em [mrdba.com.br/scripts/lab-delete-sem-where](https://mrdba.com.br/scripts/lab-delete-sem-where).

```sql
create table pedidos (
  id_pedido   number primary key,
  id_cliente  number,
  valor_bruto number(12,2),
  status      varchar2(1)
);

insert into pedidos
select level,
       1000 + mod(level,50),
       round(dbms_random.value(100,5000),2),
       case when mod(level,3)=0 then 'C' else 'A' end
  from dual connect by level <= 15482;
commit;
```

Agora o erro:

```sql
delete from pedidos;
commit;
```

```
15482 rows deleted.
Commit complete.
```

## A pergunta que define a recuperação: você commitou?

### Se você não commitou

A transação ainda está aberta e o Oracle guarda a versão anterior no undo, esperando você decidir.

```sql
ROLLBACK;
```

É isso. Os dados voltaram.

Se quiser conferir antes de desfazer, dá para consultar dentro da própria transação:

```sql
select count(*) from pedidos;
```

O resultado mostra o estado atual da sua sessão, já com o DELETE aplicado. Depois do ROLLBACK, o número volta ao que era.

### Se você commitou

Aí começa o trabalho de verdade. É onde a maioria das pessoas acha que perdeu tudo, quando na prática não perdeu.

## Flashback query: consultar a tabela como ela estava antes do delete

O Oracle consegue mostrar como a tabela estava alguns minutos atrás, usando o undo que ainda não foi sobrescrito. Vale reforçar uma coisa que confunde muita gente: isso funciona sem Flashback Database e sem ARCHIVELOG. O banco onde testei estava em NOARCHIVELOG e com `FLASHBACK_ON` em NO, e funcionou.

Primeiro, olhe antes de agir:

```sql
select count(*)
  from pedidos as of timestamp (systimestamp - interval '30' second);
```

```
COUNT(*)
--------
   15482
```

As 15482 linhas continuam lá, no undo. Agora recupere o que sumiu:

```sql
insert into pedidos
select * from pedidos as of timestamp (systimestamp - interval '30' second)
minus
select * from pedidos;
commit;

select count(*) from pedidos;
```

```
15482 rows created.
Commit complete.

COUNT(*)
--------
   15482
```

O MINUS é o detalhe que evita duplicar o que não foi apagado, porque você insere apenas a diferença entre o passado e o presente. Se o DELETE pegou só um pedaço da tabela, ou se alguém já recuperou parte das linhas, ele continua correto.

Se souber o horário exato, use timestamp explícito em vez de intervalo:

```sql
as of timestamp to_timestamp('26/07/2026 14:35:00', 'DD/MM/YYYY HH24:MI:SS')
```

## ORA-01466: quando um DDL corta a linha do tempo do flashback

Existe um limite do Flashback Query que aparece pouco na documentação e derruba gente em produção: ele não atravessa DDL.

```
ERROR at line 2:
ORA-01466: unable to read data - table definition has changed
```

Esse erro significa que o instante para onde você quer voltar é anterior a uma mudança estrutural na tabela. Um ALTER TABLE ADD COLUMN, um índice criado, um TRUNCATE: qualquer um deles corta a linha do tempo, porque naquele momento a tabela não existia com a definição atual. Você recebe ORA-01466 mesmo com undo de sobra.

Em produção isso importa quando o incidente acontece perto de uma janela de manutenção. O time subiu uma alteração de estrutura às duas da manhã, alguém apagou dados às três, e você tenta voltar para as vinte e três horas do dia anterior. Não vai.

Quando aparecer, encurte o intervalo para um ponto posterior ao último DDL. Se nem assim resolver, o Flashback Query não te atende e o caminho passa a ser o backup.

## Flashback version query: descobrir quando o delete aconteceu

Na maioria dos incidentes ninguém sabe a hora exata do estrago. O DELETE foi de manhã, alguém percebeu à tarde, e a resposta que você recebe é "acho que foi hoje cedo". O Flashback Version Query mostra o histórico de versões de cada linha dentro da janela de undo:

```sql
select versions_operation, versions_startscn, versions_endscn, id_pedido
  from pedidos versions between timestamp
       (systimestamp - interval '10' minute) and systimestamp
 where id_pedido = 7;
```

A coluna `versions_operation` traz I para insert, U para update e D para delete. Com ela você localiza o SCN exato da operação e usa esse SCN como ponto de retorno, que é bem mais preciso do que chutar horários:

```sql
select count(*) from pedidos as of scn 2847193;
```

Um aviso vindo do laboratório: a quantidade de versões que aparece depende do que ainda existe no undo e de quais linhas foram tocadas. Não estranhe se o retorno vier mais enxuto do que você esperava, principalmente se já tiver feito a recuperação antes de consultar. Consulte o histórico antes de corrigir, não depois.

## Flashback table: reverter a tabela inteira para um ponto no tempo

Se o estrago foi grande e você quer devolver a tabela ao estado anterior, existe o caminho direto:

```sql
alter table pedidos enable row movement;

flashback table pedidos
to timestamp (systimestamp - interval '30' second);
```

Cuidado com esse aqui, porque ele desfaz tudo que aconteceu na tabela nesse intervalo, não apenas o seu DELETE. Se outras pessoas inseriram dados legítimos nesse meio tempo, esses dados também vão embora. Em produção eu prefiro o INSERT com MINUS que mostrei acima, que é cirúrgico.

## Undo retention: por que 900 segundos não salvam ninguém

Chegamos no ponto que separa quem foi salvo pelo Flashback de quem descobriu que ele não estava lá.

Numa instalação 19c padrão, o parâmetro que define quanto tempo o undo é preservado responde assim:

```sql
select name, value from v$parameter where name = 'undo_retention';
```

```
NAME              VALUE
----------------  -----
undo_retention    900
```

Novecentos segundos. Quinze minutos.

A maior parte do material sobre Flashback trata esses quinze minutos como a sua janela de trabalho. Na prática, eles só valem se o erro foi seu e você percebeu na hora.

Pense em como o incidente realmente acontece. A pessoa apaga os dados e não te avisa, porque primeiro tenta resolver sozinha. Depois de um tempo desiste e abre um chamado. O chamado entra numa fila. Alguém triagem, escala, e você é acionado. Em muitos casos o erro nem é percebido na hora: aparece dias depois, quando alguém nota que o relatório não bate.

Entre o DELETE e o seu telefone tocando passaram horas. Talvez um dia. E aí você roda o Flashback Query e recebe isto:

```
ORA-01555: snapshot too old
```

O undo já foi reciclado. A ferramenta certa existia, estava disponível, e não serviu porque ninguém dimensionou a janela antes de precisar dela.

Por isso `undo_retention` de quinze minutos não é um número aceitável em ambiente crítico. Ele precisa ser calculado com base em quanto tempo a sua operação realmente leva para detectar e escalar um erro humano, e a tablespace de undo precisa ter espaço para sustentar esse tempo. Como dimensionar isso é assunto que merece artigo próprio, e vou escrever.

Vale olhar também quanto o banco está realmente conseguindo entregar de retenção, que muda conforme o espaço disponível:

```sql
select begin_time, tuned_undoretention
  from v$undostat order by begin_time desc fetch first 3 rows only;
```

Se o Flashback não alcança mais, sobra o caminho do backup: restore para um ambiente auxiliar e extração da tabela de lá. Volta a ser o cenário de horas que a gente queria evitar, e depende de uma coisa que muita empresa só descobre nesse momento, que é se o backup realmente volta.

## Como evitar o delete sem where na próxima vez

A regra que mais evita esse problema é simples: escreva o SELECT antes do DELETE, sempre. Rode um `select count(*)` com exatamente o mesmo WHERE que você vai usar. Se o número te surpreender, você acabou de evitar o incidente.

Vale também conferir o autocommit da sua ferramenta. No SQL*Plus ele vem desligado por padrão, mas várias ferramentas gráficas vêm com autocommit ligado, e aí não existe ROLLBACK para chamar de seu.

Sobre o erro de selecionar só um pedaço do comando: acostume-se a executar por statement, não por seleção. É o hábito que evita o UPDATE sem WHERE que parecia certo na tela.

Por fim, trate produção como produção. Conexão de produção com destaque visual na ferramenta, sessão separada, atenção redobrada. Parece básico, mas boa parte desses incidentes acontece porque a pessoa achou que estava em homologação.

## O que um delete acidental revela sobre o ambiente

Todo mundo comete esse erro uma vez. A diferença entre um susto e um desastre costuma estar em decisões tomadas muito antes do incidente: o undo dimensionado para dar margem de reação e alguém que saiba qual comando rodar nos primeiros cinco minutos. Isso não se resolve durante a emergência, se resolve num dia comum em que ninguém está com pressa.

---

*Testado em Oracle Database 19c Enterprise Edition 19.3.0.0.0, instância non-CDB em NOARCHIVELOG, julho de 2026.*

Para reproduzir este e outros laboratórios você precisa de um ambiente Oracle próprio, e montar um do zero costuma travar mais gente do que o conteúdo em si. O módulo que ensina a construir esse laboratório completo é gratuito dentro do curso [Oracle Fundamentals](https://mrdba.com.br/curso-oracle-fundamentals), junto com o módulo de fundamentos do banco. É o mesmo ambiente que eu uso para validar o que escrevo aqui.

*Marcio Mandarino é DBA Oracle e SQL Server há mais de 20 anos, com passagens por ambientes críticos de varejo, saúde, educação e locação de equipamentos.*

