SQL Esvaziar uma Tabela

Começo de ano na Loja Aurora: a Ana quer zerar uma tabela de testes e começar de novo, mantendo a tabela, as colunas e as regras. Em muitos tutoriais de SQL você vai encontrar o comando TRUNCATE TABLE ("trunque", "corte a tabela") para isso. No SQLite, ele não existe. Nesta aula você vai ver o erro que ele dá, como o DELETE sem WHERE faz o mesmo serviço, por que, no SQLite, esse DELETE costuma ser tão rápido quanto um TRUNCATE, e o que muda nos outros bancos.

Você vai aprender
  • Por que TRUNCATE TABLE dá erro no SQLite, e o que usar no lugar.
  • Como esvaziar uma tabela com DELETE FROM tabela; e conferir o resultado.
  • A ordem certa para esvaziar tabelas ligadas por chaves estrangeiras.
  • A truncate optimization: o atalho que o SQLite usa para apagar tudo de uma vez, e quando ele não vale.
  • O que acontece com o contador do AUTOINCREMENT quando a tabela é esvaziada.
  • As diferenças entre esvaziar (DELETE/TRUNCATE) e jogar fora (DROP TABLE).

Esta aula junta duas que vieram antes: DELETE e O Perigo do UPDATE e DELETE sem WHERE. Os exemplos rodam de verdade no seu navegador, e cada Rodar começa do banco limpo: pode esvaziar à vontade.


A ideia: esvaziar a gaveta, ficar com a gaveta

Na aula DROP TABLE, a gaveta do fichário era arrancada e jogada fora. Agora é diferente: você tira todas as fichas, e a gaveta continua no móvel, com a etiqueta e as divisórias, pronta para receber fichas novas. Há dois jeitos de tirar as fichas:

JeitoNo fichárioNo SQL
Uma por umaVocê pega cada ficha, olha, e põe na caixa do lixo.DELETE visitando linha por linha.
De uma vezVocê vira a gaveta de cabeça para baixo na caixa.TRUNCATE nos outros bancos; no SQLite, o atalho automático do DELETE sem WHERE.

O resultado é o mesmo: gaveta vazia. A diferença é a velocidade, e o que acontece com as regras "uma por uma" (como as chaves estrangeiras) no caminho.


1. O SQLite não tem TRUNCATE

A Ana, que leu um tutorial de MySQL, tenta:

TRUNCATE TABLE itens_pedido;

near "TRUNCATE": syntax error, "erro de sintaxe perto de TRUNCATE". Para o SQLite, TRUNCATE não é uma palavra do idioma dele, assim como TRUNCATE itens_pedido; (sem o TABLE). No terminal, a seta aponta logo para a primeira palavra:

$ sqlite3 loja.db
SQLite version 3.50.4 2025-07-30 19:33:53
Enter ".help" for usage hints.
sqlite> TRUNCATE TABLE itens_pedido;
Parse error: near "TRUNCATE": syntax error
  TRUNCATE TABLE itens_pedido;
  ^--- error here
sqlite> .quit

2. O jeito do SQLite: DELETE sem WHERE

Um DELETE apaga as linhas que o WHERE escolhe. Sem WHERE, ele escolhe todas:

DELETE FROM itens_pedido;

SELECT COUNT(*) AS itens FROM itens_pedido;
DELETE FROM itens_pedido
"Apague da tabela itens_pedido". Sem WHERE, vale para todas as linhas.
;
O fim do comando. Repare que não há mais nada: é isso que transforma um DELETE comum num "esvaziar".
SELECT COUNT(*)
Confere: 0 linhas. E a tabela continua existindo; se não existisse, o SELECT daria no such table.

O quadro mostra "220 linhas afetadas": os 220 itens de pedido foram apagados. A estrutura ficou: as colunas, a chave primária, as chaves estrangeiras. A Ana pode inserir itens novos na mesma hora.

O mesmo comando que salva é o que estraga

Esvaziar uma tabela de propósito e esquecer o WHERE por engano são exatamente o mesmo comando. O banco não tem como saber qual foi a sua intenção. Por isso: escreva o DELETE sem WHERE só quando quiser esvaziar mesmo, rode antes um SELECT COUNT(*) para ver quantas linhas vão embora, e, num banco com dados de verdade, faça dentro de uma transação ou depois de um backup (aula O Perigo do UPDATE e DELETE sem WHERE).

Experimente: o código usa o TRUNCATE de outros bancos e dá erro. Troque a primeira linha pelo jeito do SQLite de esvaziar a tabela itens_pedido, sem apagar a tabela.

TRUNCATE TABLE itens_pedido;

SELECT COUNT(*) AS itens FROM itens_pedido;
DELETE FROM itens_pedido;

SELECT COUNT(*) AS itens FROM itens_pedido;

3. Esvaziar tabelas ligadas: a ordem importa

Agora a Ana quer esvaziar os pedidos:

DELETE FROM pedidos;

FOREIGN KEY constraint failed. Os itens apontam para os pedidos (aula FOREIGN KEY), e apagar os pedidos deixaria 220 itens órfãos. Com a regra ligada, como no editor, o banco recusa, e nenhum pedido é apagado: ou vão todos, ou nenhum. A ordem certa é a mesma do DROP TABLE: primeiro as filhas, depois as mães.

DELETE FROM itens_pedido;
DELETE FROM pedidos;

SELECT COUNT(*) AS pedidos FROM pedidos;

220 itens, depois 90 pedidos. Os clientes, os produtos e os funcionários continuam lá. (Se a chave estrangeira tivesse ON DELETE CASCADE, o DELETE FROM pedidos levaria os itens junto, sozinho; a Loja Aurora não usa isso.)


4. A truncate optimization: por que o DELETE é rápido

Nos outros bancos, o TRUNCATE existe porque é muito mais rápido que o DELETE: ele joga fora as páginas de dados de uma vez, em vez de apagar linha por linha. O SQLite não precisa de um comando separado para isso. A documentação dele explica:

Na documentação do SQLite

"Quando o WHERE e o RETURNING são omitidos de um DELETE e a tabela não tem gatilhos, o SQLite usa uma otimização para apagar todo o conteúdo da tabela sem precisar visitar cada linha individualmente. Essa otimização de "truncar" deixa o DELETE muito mais rápido."

Ou seja: o DELETE FROM t; do SQLite já é o TRUNCATE, quando dá. Dá para ver a diferença por dentro com o EXPLAIN, que mostra o "programa" que o SQLite monta para rodar um comando (aula EXPLAIN QUERY PLAN). Não precisa entender cada linha; procure a coluna opcode (a instrução). Primeiro, numa cópia dos clientes, sem WHERE:

CREATE TABLE copia AS SELECT * FROM clientes;

EXPLAIN DELETE FROM copia;

Cinco instruções, e a do meio é Clear ("limpe"): a tabela inteira de uma vez. Agora o mesmo DELETE com um WHERE que também pega todas as linhas:

CREATE TABLE copia AS SELECT * FROM clientes;

EXPLAIN DELETE FROM copia WHERE id > 0;

Treze instruções e nenhum Clear: Rewind (vá para a primeira linha), Column e Le (leia o id e compare), Delete (apague esta linha), Next (vá para a próxima) e volta. É o "uma por uma". Numa tabela de 30 linhas, a diferença nem se sente; numa de 3 milhões, sente-se muito.

Quando o atalho não vale

O SQLite só pode virar a gaveta de uma vez quando ninguém precisa olhar as fichas uma por uma. Ele apaga linha por linha quando:

SituaçãoPor quê
O DELETE tem WHERE (qualquer um, até WHERE 1)Cada linha precisa ser testada.
O DELETE tem RETURNINGCada linha apagada precisa ser devolvida.
A tabela tem gatilhos (triggers)O gatilho precisa rodar para cada linha.
As chaves estrangeiras estão ligadas e a tabela participa de algumaCada linha precisa ser conferida. No editor (regra ligada), EXPLAIN DELETE FROM itens_pedido; não mostra Clear; com PRAGMA foreign_keys = OFF; antes, mostra.

Nos três primeiros casos, a documentação do SQLite é explícita. O quarto você acabou de conferir pelo EXPLAIN. Em todos eles o resultado é o mesmo, só mais lento. E o número de "linhas afetadas" vem certo nos dois caminhos: desde a versão 3.6.5 (de 2008), mesmo com o atalho o SQLite conta as linhas apagadas.

E o espaço no disco?

Esvaziar uma tabela libera espaço dentro do arquivo do banco, que o SQLite reaproveita nas próximas gravações. Se o arquivo vai ou não encolher, depende de como o banco foi configurado; o comando que devolve o espaço ao disco é o VACUUM, explicado em VACUUM e ANALYZE.


5. E o contador do AUTOINCREMENT?

Esvaziar a tabela não zera o contador do AUTOINCREMENT (aula AUTOINCREMENT). Ele fica guardado à parte, na tabela sqlite_sequence, e o DELETE não mexe nela:

CREATE TABLE cupons (
    id     INTEGER PRIMARY KEY AUTOINCREMENT,
    codigo TEXT NOT NULL
);
INSERT INTO cupons (codigo) VALUES ('BEMVINDO'), ('NATAL'), ('PIAUI');

DELETE FROM cupons;

INSERT INTO cupons (codigo) VALUES ('VERAO');
SELECT * FROM cupons;

O VERAO é o 4, mesmo com a tabela vazia. Na maioria das vezes, é isso mesmo que você quer (é para isso que serve o AUTOINCREMENT). Se for uma tabela de testes e você quiser recomeçar do 1, apague também a linha dela na sqlite_sequence, com a tabela já vazia:

CREATE TABLE cupons (
    id     INTEGER PRIMARY KEY AUTOINCREMENT,
    codigo TEXT NOT NULL
);
INSERT INTO cupons (codigo) VALUES ('BEMVINDO'), ('NATAL'), ('PIAUI');

DELETE FROM cupons;
DELETE FROM sqlite_sequence WHERE name = 'cupons';

INSERT INTO cupons (codigo) VALUES ('VERAO');
SELECT * FROM cupons;

Numa tabela só com INTEGER PRIMARY KEY (sem AUTOINCREMENT), não há contador nenhum: uma tabela vazia recomeça do 1 sozinha.


6. Esvaziar ou jogar fora? As formas de fazer

FormaEscrita no SQLiteO que sobraQuando usar
EsvaziarDELETE FROM t;A tabela vazia, com tudo: colunas, regras, índices, gatilhos.O jeito normal de "zerar" uma tabela.
Esvaziar e zerar o contadorDELETE FROM t; + DELETE FROM sqlite_sequence WHERE name = 't';A tabela vazia, numerando do 1.Tabelas de teste com AUTOINCREMENT.
Apagar só algumasDELETE FROM t WHERE …;As outras linhas.Quase sempre (aula DELETE).
Jogar fora e recriarDROP TABLE t; + CREATE TABLE t (…);Uma tabela nova; índices e gatilhos precisam ser recriados.Quando a estrutura também vai mudar (aula ALTER TABLE).
Jogar foraDROP TABLE t;Nada.A tabela não serve mais (aula DROP TABLE).

Quando dá errado

TRUNCATE no SQLite

near "TRUNCATE": syntax error (seção 1).

Como consertar: DELETE FROM itens_pedido;.

O asterisco do Access

Quem vem do Microsoft Access escreve um asterisco, como num SELECT:

DELETE * FROM itens_pedido;

A mensagem: near "*": syntax error. O DELETE apaga linhas inteiras; não existe escolher colunas, então não há o que pôr entre DELETE e FROM.

Como consertar: DELETE FROM itens_pedido;.

DELETE TABLE
DELETE TABLE itens_pedido;

A mensagem: near "TABLE": syntax error. Uma mistura de DELETE FROM com DROP TABLE.

Como consertar: para esvaziar, DELETE FROM itens_pedido;; para jogar fora, DROP TABLE itens_pedido;.

Esvaziar uma tabela mãe

FOREIGN KEY constraint failed (seção 3).

Como consertar: esvazie antes as tabelas filhas. Desligar as chaves estrangeiras para "passar" também funciona, mas deixa o banco cheio de órfãos: não faça.

Esvaziou a tabela errada

Não há mensagem nenhuma: o DELETE deu certo, na tabela errada. Fora de uma transação, os dados só voltam de um backup. Dentro de uma, ROLLBACK desfaz:

BEGIN;
DELETE FROM itens_pedido;
ROLLBACK;

SELECT COUNT(*) AS itens FROM itens_pedido;

Os 220 itens voltaram. Veja Transações e ROLLBACK.


Nos outros bancos

MySQL, PostgreSQL e SQL Server têm TRUNCATE TABLE, e lá ele é bem diferente de um DELETE (trechos ilustrativos, não rodam no SQLite):

-- MySQL, PostgreSQL e SQL Server
TRUNCATE TABLE itens_pedido;

-- PostgreSQL: várias tabelas, zerando os contadores, levando as filhas junto
TRUNCATE itens_pedido, pedidos RESTART IDENTITY CASCADE;
MySQLPostgreSQLSQL Server
Zera o contador automáticoSim, sempreSó com RESTART IDENTITY (o padrão é continuar)Sim, volta ao valor inicial
Tabela que outras referenciamRecusa (InnoDB)Recusa, a não ser com CASCADERecusa
Dá para desfazer com ROLLBACKNão: confirma a transação sozinhoSimSim
Dispara os gatilhos de DELETENãoNão (tem gatilhos próprios de TRUNCATE)Não

No MySQL, a documentação avisa ainda que o TRUNCATE não informa quantas linhas apagou: o "0 rows affected" quer dizer "sem informação". Nos três, o DELETE FROM tabela; também funciona, linha por linha, com gatilhos e transação.


Referência rápida

EscritaO que faz
DELETE FROM t;Esvazia a tabela; ela continua existindo. No SQLite, usa o atalho de "truncar" quando pode.
DELETE FROM sqlite_sequence WHERE name = 't';Zera o contador do AUTOINCREMENT de t (só com t vazia).
EXPLAIN DELETE FROM t;Mostra o programa interno; Clear = apagou tudo de uma vez.
Ordem com chaves estrangeirasFilhas antes das mães: itens_pedido, depois pedidos.
TRUNCATE TABLE t;Não existe no SQLite. Existe no MySQL, PostgreSQL e SQL Server.
MensagemQuer dizer
near "TRUNCATE": syntax errorO SQLite não tem TRUNCATE: use DELETE FROM t;.
near "*": syntax errorDELETE * não existe: é DELETE FROM t;.
near "TABLE": syntax errorDELETE TABLE não existe: é DELETE FROM ou DROP TABLE.
FOREIGN KEY constraint failedOutra tabela aponta para esta: esvazie a filha antes.

Exercícios

1. Esvaziar no SQLite

Complete o comando que esvazia a tabela pedidos (depois dos itens):

FROM pedidos;

2. A instrução do atalho

No resultado do EXPLAIN DELETE FROM copia;, qual é o opcode que mostra que a tabela foi limpa de uma vez?

opcode:

3. O que o SQLite responde a TRUNCATE TABLE itens_pedido;?

4. Em qual destes casos o SQLite não usa o atalho de apagar tudo de uma vez?

5. A tabela cupons tem AUTOINCREMENT e os ids 1, 2 e 3. Depois de DELETE FROM cupons;, que id ganha o próximo cupom?

6. Qual é a diferença entre esvaziar (DELETE FROM t;) e DROP TABLE t;?

Missão final: zerar os pedidos

Missão: a Ana quer esvaziar a tabela pedidos para recomeçar o ano, mas o código dá FOREIGN KEY constraint failed. Conserte, esvaziando as tabelas na ordem certa. Os clientes têm de continuar todos lá (são 30).

DELETE FROM pedidos;

SELECT COUNT(*) AS pedidos FROM pedidos;
DELETE FROM itens_pedido;
DELETE FROM pedidos;

SELECT COUNT(*) AS pedidos FROM pedidos;

Resumo

  • O SQLite não tem TRUNCATE: TRUNCATE TABLE t; dá near "TRUNCATE": syntax error.
  • Para esvaziar uma tabela e mantê-la, use DELETE FROM t;, sem WHERE. Confira antes com SELECT COUNT(*): o mesmo comando, por engano, é um desastre.
  • Sem WHERE, sem RETURNING e sem gatilhos, o SQLite usa a truncate optimization: apaga tudo de uma vez (o Clear do EXPLAIN), tão rápido quanto um TRUNCATE. Com as chaves estrangeiras ligadas e a tabela numa delas, ele vai linha por linha.
  • Com chaves estrangeiras, esvazie das filhas para as mães.
  • O DELETE não zera o contador do AUTOINCREMENT; para isso, apague a linha da tabela na sqlite_sequence.
  • Nos outros bancos, TRUNCATE TABLE existe, mas com regras próprias: zera contadores, recusa tabelas referenciadas e, no MySQL, não pode ser desfeito.