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.
- Por que
TRUNCATE TABLEdá 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
AUTOINCREMENTquando 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:
| Jeito | No fichário | No SQL |
|---|---|---|
| Uma por uma | Você pega cada ficha, olha, e põe na caixa do lixo. | DELETE visitando linha por linha. |
| De uma vez | Você 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
DELETEcomum num "esvaziar". - SELECT COUNT(*)
- Confere: 0 linhas. E a tabela continua existindo; se não existisse, o
SELECTdariano 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.
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:
"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ção | Por quê |
|---|---|
O DELETE tem WHERE (qualquer um, até WHERE 1) | Cada linha precisa ser testada. |
O DELETE tem RETURNING | Cada 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 alguma | Cada 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.
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
| Forma | Escrita no SQLite | O que sobra | Quando usar |
|---|---|---|---|
| Esvaziar | DELETE FROM t; | A tabela vazia, com tudo: colunas, regras, índices, gatilhos. | O jeito normal de "zerar" uma tabela. |
| Esvaziar e zerar o contador | DELETE FROM t; + DELETE FROM sqlite_sequence WHERE name = 't'; | A tabela vazia, numerando do 1. | Tabelas de teste com AUTOINCREMENT. |
| Apagar só algumas | DELETE FROM t WHERE …; | As outras linhas. | Quase sempre (aula DELETE). |
| Jogar fora e recriar | DROP 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 fora | DROP TABLE t; | Nada. | A tabela não serve mais (aula DROP TABLE). |
Quando dá errado
near "TRUNCATE": syntax error (seção 1).
Como consertar: DELETE FROM itens_pedido;.
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 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;.
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.
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.
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;
| MySQL | PostgreSQL | SQL Server | |
|---|---|---|---|
| Zera o contador automático | Sim, sempre | Só com RESTART IDENTITY (o padrão é continuar) | Sim, volta ao valor inicial |
| Tabela que outras referenciam | Recusa (InnoDB) | Recusa, a não ser com CASCADE | Recusa |
Dá para desfazer com ROLLBACK | Não: confirma a transação sozinho | Sim | Sim |
Dispara os gatilhos de DELETE | Não | Nã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
| Escrita | O 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 estrangeiras | Filhas antes das mães: itens_pedido, depois pedidos. |
TRUNCATE TABLE t; | Não existe no SQLite. Existe no MySQL, PostgreSQL e SQL Server. |
| Mensagem | Quer dizer |
|---|---|
near "TRUNCATE": syntax error | O SQLite não tem TRUNCATE: use DELETE FROM t;. |
near "*": syntax error | DELETE * não existe: é DELETE FROM t;. |
near "TABLE": syntax error | DELETE TABLE não existe: é DELETE FROM ou DROP TABLE. |
FOREIGN KEY constraint failed | Outra 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):
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?
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;, semWHERE. Confira antes comSELECT COUNT(*): o mesmo comando, por engano, é um desastre. - Sem
WHERE, semRETURNINGe sem gatilhos, o SQLite usa a truncate optimization: apaga tudo de uma vez (oCleardoEXPLAIN), tão rápido quanto umTRUNCATE. 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
DELETEnão zera o contador doAUTOINCREMENT; para isso, apague a linha da tabela nasqlite_sequence. - Nos outros bancos,
TRUNCATE TABLEexiste, mas com regras próprias: zera contadores, recusa tabelas referenciadas e, no MySQL, não pode ser desfeito.