SQL DROP TABLE

Às vezes uma tabela não serve mais: era um teste, uma cópia temporária, uma ideia que não deu certo. Para jogá-la fora inteira, com a estrutura e todas as linhas, existe o DROP TABLE ("solte", "largue a tabela"). É um dos comandos mais curtos do SQL e um dos mais perigosos: o banco não pergunta "tem certeza?" e não existe lixeira. Nesta aula você vai apagar tabelas da Loja Aurora (no editor, cada Rodar traz tudo de volta), ver por que o banco às vezes se recusa e aprender a se proteger.

Você vai aprender
  • Como apagar uma tabela com DROP TABLE, e a diferença para o DELETE.
  • DROP TABLE IF EXISTS: apagar só se existir, sem erro.
  • Por que o SQLite recusa apagar uma tabela que outras referenciam, e a ordem certa de apagar.
  • O que some junto (os índices) e o que fica quebrado (as views).
  • Como se proteger: transação, cópia e backup.
  • Os erros reais: no such table, FOREIGN KEY constraint failed, use DROP VIEW…

A ideia: jogar fora a gaveta

Imagine o fichário da loja, com uma gaveta para cada assunto: clientes, produtos, pedidos. Há duas coisas bem diferentes que você pode fazer com uma gaveta:

No fichárioNo SQLDepois
Tirar todas as fichas e deixar a gaveta vazia no lugarDELETE FROM tabela;A tabela existe, com as colunas, e tem 0 linhas. Dá para pôr fichas novas.
Arrancar a gaveta do móvel e jogar fora, com fichas e tudoDROP TABLE tabela;A tabela não existe mais. Para usar de novo, é preciso criá-la do zero.

Esvaziar a tabela tem aula própria: Esvaziar uma Tabela. Esta aula é sobre jogar a gaveta fora.


1. O primeiro DROP TABLE

A Ana criou uma tabela de rascunho para testar os cupons, e agora não precisa mais dela:

CREATE TABLE rascunho (
    id     INTEGER PRIMARY KEY,
    codigo TEXT
);
INSERT INTO rascunho (codigo) VALUES ('TESTE1'), ('TESTE2');

DROP TABLE rascunho;
DROP TABLE
"Jogue fora a tabela". Apaga a tabela inteira: as colunas, as regras e todas as linhas.
rascunho
O nome da tabela. Um só por comando, no SQLite.

O quadro diz só "DROP TABLE: ok". Não diz quantas linhas foram embora, nem pede confirmação. E se a Ana tentar usar a tabela depois?

CREATE TABLE rascunho (
    id     INTEGER PRIMARY KEY,
    codigo TEXT
);
INSERT INTO rascunho (codigo) VALUES ('TESTE1'), ('TESTE2');

DROP TABLE rascunho;

SELECT * FROM rascunho;

no such table: rascunho, "não existe a tabela rascunho". Para o banco, é como se ela nunca tivesse existido.

No terminal

No banco da Ana, no computador dela, o efeito aparece no .tables. Repare que o DROP TABLE não mostra nada quando dá certo:

$ sqlite3 loja.db
SQLite version 3.50.4 2025-07-30 19:33:53
Enter ".help" for usage hints.
sqlite> .tables
categorias    fornecedores  itens_pedido  produtos
clientes      funcionarios  pedidos
sqlite> DROP TABLE itens_pedido;
sqlite> .tables
categorias    fornecedores  pedidos
clientes      funcionarios  produtos
sqlite> SELECT COUNT(*) FROM itens_pedido;
Parse error: no such table: itens_pedido
sqlite> .quit

Os 220 itens de pedido foram embora. Ao contrário do editor, aqui é o arquivo loja.db de verdade: não volta mais. (Para a Ana, a sorte é que esse banco é uma cópia de estudo.)


2. DROP TABLE IF EXISTS

Apagar uma tabela que não existe é erro:

DROP TABLE cupons;

Num roteiro que recria as tabelas do zero, esse erro atrapalha: na primeira vez, a tabela ainda não existe, e o roteiro para no primeiro comando. Para isso há o IF EXISTS ("se existir"):

DROP TABLE IF EXISTS cupons;

CREATE TABLE cupons (
    id       INTEGER PRIMARY KEY,
    codigo   TEXT NOT NULL,
    desconto REAL NOT NULL
);
INSERT INTO cupons (codigo, desconto) VALUES ('NATAL', 15);

SELECT * FROM cupons;
IF EXISTS
"Se existir": se a tabela existe, apaga; se não existe, não faz nada e não dá erro. O roteiro segue em frente.

Esse roteiro pode rodar quantas vezes você quiser: cada vez, ele joga fora a tabela velha (se houver) e cria uma nova, limpinha. É exatamente o que faz o arquivo que monta a Loja Aurora (o aurora.sql): ele começa com sete linhas DROP TABLE IF EXISTS, uma por tabela, antes dos CREATE TABLE.

O par do IF EXISTS

O contrário também existe: CREATE TABLE IF NOT EXISTS cupons (…) cria a tabela só se ela ainda não existir (aula CREATE TABLE). Sem ele, criar uma tabela que já existe dá table cupons already exists.


3. Quando o banco se recusa: as chaves estrangeiras

A Ana tenta apagar a tabela de pedidos:

DROP TABLE pedidos;

FOREIGN KEY constraint failed. A tabela itens_pedido tem uma chave estrangeira apontando para pedidos (aula FOREIGN KEY). Com a regra ligada, como no editor, o DROP TABLE primeiro apaga todas as linhas da tabela, e isso deixaria 220 itens apontando para pedidos que não existem. O banco recusa, e a tabela pedidos continua inteira. O IF EXISTS não ajuda aqui: DROP TABLE IF EXISTS pedidos; dá o mesmo erro, porque a tabela existe.

A ordem certa: dos filhos para as mães

Para apagar várias tabelas ligadas, comece pela que ninguém referencia e vá subindo:

DROP TABLE itens_pedido;   -- ninguém aponta para os itens
DROP TABLE pedidos;        -- agora ninguém aponta para os pedidos
DROP TABLE clientes;       -- nem para os clientes

SELECT name
FROM sqlite_schema
WHERE type = 'table';

Sobraram quatro tabelas. Na Loja Aurora, as setas das chaves estrangeiras são estas (a filha à esquerda, as mães à direita):

itens_pedido  →  pedidos, produtos
pedidos       →  clientes, funcionarios
produtos      →  categorias, fornecedores
funcionarios  →  funcionarios (o chefe)

Para apagar tudo, comece por quem está só à esquerda (itens_pedido) e termine pelas que só aparecem à direita (clientes, categorias, fornecedores).

Experimente: o código tenta apagar pedidos e depois itens_pedido, e para no primeiro comando com FOREIGN KEY constraint failed. Troque a ordem dos dois DROP TABLE. O SELECT do fim vai dar erro, e esse erro é a prova de que deu certo: no such table: pedidos.

DROP TABLE pedidos;
DROP TABLE itens_pedido;

SELECT COUNT(*) FROM pedidos;
DROP TABLE itens_pedido;
DROP TABLE pedidos;

SELECT COUNT(*) FROM pedidos;

No terminal, a regra vem desligada

Lembra que, no programa sqlite3, as chaves estrangeiras vêm desligadas (aula FOREIGN KEY)? Então lá o DROP TABLE de uma tabela mãe passa sem reclamar, e deixa os filhos órfãos:

$ sqlite3 loja.db
SQLite version 3.50.4 2025-07-30 19:33:53
Enter ".help" for usage hints.
sqlite> DROP TABLE clientes;
sqlite> SELECT COUNT(*) FROM pedidos;
90
sqlite> SELECT COUNT(*) FROM clientes;
Parse error: no such table: clientes
sqlite> .quit

Os 90 pedidos continuam lá, apontando para clientes que não existem mais. Mais um motivo para começar toda sessão com PRAGMA foreign_keys = ON;.


4. O que vai junto e o que fica quebrado

Os índices vão junto

Um índice (aula Índices) pertence a uma tabela. Quando a tabela vai embora, os índices dela também:

CREATE INDEX idx_quantidade ON itens_pedido (quantidade);

DROP TABLE itens_pedido;

SELECT name, type
FROM sqlite_schema
WHERE name = 'idx_quantidade';

Nenhuma linha: o índice sumiu junto. O mesmo vale para os gatilhos (triggers) da tabela.

As views ficam quebradas

Uma view (aula Views) é uma consulta salva com nome. Ela não pertence a uma tabela; ela só usa a tabela. Por isso o SQLite não a apaga junto, e ela fica quebrada:

CREATE VIEW v_itens AS
SELECT pedido_id, quantidade FROM itens_pedido;

DROP TABLE itens_pedido;

SELECT * FROM v_itens;

O DROP TABLE funcionou sem aviso; o erro só apareceu ao usar a view: no such table: main.itens_pedido (main é o nome do banco aberto). Antes de apagar uma tabela, confira quem a usa: SELECT name, type FROM sqlite_schema WHERE sql LIKE '%itens_pedido%';.


5. Como se proteger

Não existe "desfazer" para um DROP TABLE confirmado. Mas existem três redes de segurança, da mais rápida para a mais completa:

Dentro de uma transação

Entre um BEGIN e um COMMIT, nada é definitivo (aula Transações). Se você se arrepender antes do COMMIT, o ROLLBACK ("volte atrás") desfaz tudo, inclusive o DROP TABLE:

BEGIN;

DROP TABLE itens_pedido;

ROLLBACK;

SELECT COUNT(*) AS itens FROM itens_pedido;

Os 220 itens estão de volta. No SQLite, até os comandos que mudam a estrutura (CREATE, ALTER, DROP) respeitam a transação; em alguns outros bancos, não (veja a nota no fim).

Uma cópia antes

Se a tabela pode fazer falta, guarde uma cópia numa tabela nova antes de apagar: CREATE TABLE itens_pedido_copia AS SELECT * FROM itens_pedido; (aula Criar Tabela a partir de Consulta).

Um backup do arquivo

A proteção completa é uma cópia do banco inteiro, feita antes de qualquer mudança grande. No SQLite, o banco é um arquivo, e o sqlite3 tem comandos próprios para isso (aula Backup).

Rede de segurançaProtege contraQuando usar
BEGIN … ROLLBACKArrependimento dentro da mesma sessão, antes do COMMITRoteiros com vários passos, como a recriação de tabela da aula ALTER TABLE
CREATE TABLE … AS SELECTPrecisar dos dados de uma tabela depoisAntes de apagar uma tabela que você "acha" que não serve mais
Backup do arquivoQualquer coisa, inclusive o que você não previuSempre, antes de mexer num banco que tem dados de verdade

6. DROP TABLE, DELETE ou DROP COLUMN?

Você querUseO que sobra
Jogar fora a tabela inteiraDROP TABLE t;Nada.
Apagar algumas linhasDELETE FROM t WHERE …; (aula DELETE)A tabela e as outras linhas.
Apagar todas as linhas, mantendo a tabelaDELETE FROM t; (aula Esvaziar uma Tabela)A tabela vazia, com as colunas e regras.
Tirar uma colunaALTER TABLE t DROP COLUMN c; (aula ALTER TABLE)A tabela, sem aquela coluna.
Apagar uma viewDROP VIEW v;As tabelas que ela usava.
Apagar um índiceDROP INDEX i;A tabela e os dados.

Quando dá errado

no such table

A tabela não existe, ou o nome está errado, ou ela já foi apagada (por exemplo, rodando o mesmo roteiro duas vezes). Veja o exemplo da seção 2.

Como consertar: confira o nome com SELECT name FROM sqlite_schema WHERE type = 'table'; (ou .tables, no terminal). Se o roteiro precisa rodar várias vezes, use DROP TABLE IF EXISTS.

FOREIGN KEY constraint failed

Outra tabela aponta para esta (seção 3).

Como consertar: apague antes as tabelas filhas (as que têm REFERENCES para esta). Para ver quem aponta para quem, use PRAGMA foreign_key_list(itens_pedido); em cada tabela.

Várias tabelas num comando só
DROP TABLE itens_pedido, pedidos;

A mensagem: near ",": syntax error. O SQLite apaga uma tabela por comando. (O MySQL e o PostgreSQL aceitam a lista; veja a nota abaixo.)

Como consertar: um DROP TABLE para cada tabela, na ordem certa.

Tentou apagar uma view com DROP TABLE
CREATE VIEW clientes_pi AS
SELECT nome FROM clientes WHERE estado = 'PI';

DROP TABLE clientes_pi;

A mensagem: use DROP VIEW to delete view clientes_pi, "use DROP VIEW para apagar a view clientes_pi". O SQLite até ensina o comando certo.

Tentou apagar a tabela interna do SQLite
DROP TABLE sqlite_schema;

A mensagem: table sqlite_master may not be dropped, "a tabela sqlite_master não pode ser apagada". sqlite_schema e sqlite_master são dois nomes para a mesma tabela interna, a que guarda a descrição de todas as outras. Sem ela, o banco não saberia o que tem dentro.

DROP TABLES, no plural
DROP TABLES rascunho;

A mensagem: near "TABLES": syntax error. É sempre TABLE, no singular, mesmo que a tabela tenha um nome no plural.


Nos outros bancos

DROP TABLE e DROP TABLE IF EXISTS existem no MySQL, no PostgreSQL e no SQL Server (neste, o IF EXISTS chegou na versão 2016). Algumas diferenças (trechos ilustrativos, não rodam no SQLite):

-- MySQL e PostgreSQL: várias tabelas num comando
DROP TABLE IF EXISTS itens_pedido, pedidos;

-- PostgreSQL: CASCADE apaga junto o que depende da tabela
-- (views e as chaves estrangeiras de outras tabelas, não as outras tabelas)
DROP TABLE pedidos CASCADE;

E um cuidado importante: no MySQL, um DROP TABLE confirma sozinho a transação aberta; um ROLLBACK depois dele não traz a tabela de volta. No PostgreSQL e no SQL Server, assim como no SQLite, o DROP TABLE dentro de uma transação pode ser desfeito.


Referência rápida

EscritaO que faz
DROP TABLE t;Apaga a tabela inteira: estrutura, regras, linhas, índices e gatilhos.
DROP TABLE IF EXISTS t;Apaga se existir; se não existir, não faz nada e não dá erro.
BEGIN; DROP TABLE t; ROLLBACK;Desfaz o DROP antes de confirmar.
DROP VIEW v; / DROP INDEX i;Apagam uma view ou um índice.
Ordem com chaves estrangeirasPrimeiro as filhas, depois as mães: itens_pedido → pedidos → clientes.
MensagemQuer dizer
no such table: cuponsA tabela não existe (use IF EXISTS se isso for normal).
FOREIGN KEY constraint failedOutra tabela aponta para esta: apague a filha antes.
near ",": syntax errorO SQLite não aceita lista de tabelas no DROP TABLE.
use DROP VIEW to delete view clientes_piÉ uma view: use DROP VIEW.
table sqlite_master may not be droppedA tabela interna do SQLite não pode ser apagada.
no such table: main.itens_pedido (ao usar uma view)A view usa uma tabela que foi apagada.

Exercícios

1. Sem erro se não existir

Complete para apagar a tabela rascunho só se ela existir:

DROP TABLE rascunho;

2. A view

Complete o comando que apaga a view clientes_pi:

DROP clientes_pi;

3. Qual é a diferença entre DELETE FROM cupons; e DROP TABLE cupons;?

4. No editor do guia, DROP TABLE clientes; dá FOREIGN KEY constraint failed. Por quê?

5. A Ana rodou BEGIN; DROP TABLE itens_pedido; ROLLBACK;. Quantos itens existem depois?

6. Existe uma view v_itens que usa itens_pedido. O que acontece com ela depois de DROP TABLE itens_pedido;?

Missão final: um roteiro que roda sempre

Missão: este roteiro deveria recriar a tabela de cupons do zero, mas para logo no primeiro comando, porque a tabela ainda não existe. Conserte só a primeira linha para que o roteiro funcione sempre: exista a tabela ou não.

DROP TABLE cupons;

CREATE TABLE cupons (
    id       INTEGER PRIMARY KEY,
    codigo   TEXT NOT NULL,
    desconto REAL NOT NULL
);
INSERT INTO cupons (codigo, desconto) VALUES ('NATAL', 15);

SELECT codigo, desconto FROM cupons;
DROP TABLE IF EXISTS cupons;

CREATE TABLE cupons (
    id       INTEGER PRIMARY KEY,
    codigo   TEXT NOT NULL,
    desconto REAL NOT NULL
);
INSERT INTO cupons (codigo, desconto) VALUES ('NATAL', 15);

SELECT codigo, desconto FROM cupons;

Resumo

  • DROP TABLE t; joga fora a tabela inteira: estrutura, regras, linhas, índices e gatilhos. Não pede confirmação e não tem lixeira.
  • DROP TABLE IF EXISTS t; não dá erro se a tabela não existir: ideal para roteiros que recriam tabelas.
  • Com as chaves estrangeiras ligadas, apagar uma tabela que outras referenciam dá FOREIGN KEY constraint failed: apague das filhas para as mães. No terminal, com a regra desligada, o DROP passa e deixa órfãos.
  • Views que usavam a tabela não somem: ficam quebradas, com no such table: main.….
  • Proteja-se com transação (BEGIN … ROLLBACK), cópia (CREATE TABLE … AS SELECT) e backup.
  • Para esvaziar sem jogar fora, use DELETE FROM t; (próxima aula).