SQL Backup: .dump e .backup
Todo o cadastro da Loja Aurora (clientes, produtos, 90 pedidos) mora num arquivo só: ~/loja.db. Se o notebook da Ana quebrar, ou se alguém rodar um DELETE sem WHERE, a loja perde tudo. A proteção é uma cópia de segurança, o backup. Nesta aula você vai ver os dois jeitos que o sqlite3 oferece: o .dump, que transforma o banco num arquivo de SQL que qualquer pessoa consegue ler, e o .backup, que faz uma cópia exata do arquivo do banco. E vai ver como trazer os dados de volta.
- Por que fazer backup, e por que copiar o arquivo com o banco aberto pode dar errado.
- O
.dump: o banco inteiro (ou uma tabela) escrito como comandos SQL. - A salvar o dump num arquivo (
sqlite3 loja.db .dump > backup.sql) e a recriar o banco a partir dele. - O
.backupe o.restore: cópia exata do banco e volta da cópia. - O
VACUUM INTO, que faz a cópia com um comando SQL. - As armadilhas reais: restaurar por cima de um banco cheio e o
.restoreque apaga tudo.
Como a aula anterior, Importar e Exportar CSV, esta é uma aula de terminal: backup é gravar arquivos, e o editor do guia não grava arquivos no seu computador. As sessões saíram do terminal da Ana, na pasta pessoal dela (~), onde está o loja.db. O editor entra para mostrar que um dump é SQL comum, que roda em qualquer lugar.
A ideia: a foto e a receita
Imagine que a Ana quer guardar uma cópia de um bolo que deu muito certo. Ela tem dois jeitos:
| Jeito | Na cozinha | No banco |
|---|---|---|
| A receita | Anota cada ingrediente e cada passo. Qualquer pessoa lê e faz o bolo de novo, em qualquer cozinha. | .dump: um arquivo de texto com os CREATE TABLE e os INSERT que recriam o banco. |
| O congelador | Guarda um bolo igualzinho no freezer. É só descongelar, sem refazer nada. | .backup: uma cópia exata do arquivo .db, pronta para abrir. |
A receita é ótima para ler, comparar e levar para outro banco. O congelador é mais rápido de fazer e de usar. Muita gente guarda os dois.
cp loja.db copia.db?
Copiar o arquivo com o cp funciona quando nenhum programa está mexendo no banco. Se um programa estiver gravando bem na hora da cópia, a cópia pode sair com metade de uma alteração, e virar um arquivo estragado. Além disso, num banco no modo WAL (veja PRAGMAs Úteis), parte dos dados recentes fica num segundo arquivo, o loja.db-wal, que o cp de um arquivo só deixa para trás. O .backup e o VACUUM INTO pedem licença ao banco e copiam um retrato consistente. Prefira os dois.
1. O .dump: o banco virado SQL
Comece por uma tabela pequena. Com o nome de uma tabela, o .dump mostra só ela:
$ sqlite3 loja.db
SQLite version 3.50.4 2025-07-30 19:33:53
Enter ".help" for usage hints.
sqlite> .dump categorias
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE categorias (
id INTEGER PRIMARY KEY,
nome TEXT NOT NULL,
descricao TEXT
);
INSERT INTO categorias VALUES(1,'Papelaria','Material escolar e de escritório');
INSERT INTO categorias VALUES(2,'Eletrônicos','Acessórios e dispositivos eletrônicos');
INSERT INTO categorias VALUES(3,'Casa e Cozinha','Utensílios para o lar');
INSERT INTO categorias VALUES(4,'Livros','Livros, dicionários e guias');
INSERT INTO categorias VALUES(5,'Brinquedos','Brinquedos e jogos para todas as idades');
INSERT INTO categorias VALUES(6,'Esporte e Lazer','Artigos esportivos e de lazer');
INSERT INTO categorias VALUES(7,'Alimentos','Alimentos não perecíveis e regionais');
INSERT INTO categorias VALUES(8,'Jardim','Ferramentas e artigos de jardinagem');
COMMIT;
sqlite> .quit
Tudo o que saiu é SQL que você já conhece (ou vai conhecer). Leia de cima para baixo:
- PRAGMA foreign_keys=OFF;
- Desliga a conferência de chaves estrangeiras durante a restauração. Assim a ordem das tabelas não importa: um produto pode entrar antes da sua categoria. (PRAGMAs têm aula própria: PRAGMAs Úteis.)
- BEGIN TRANSACTION;
- Abre uma transação: tudo até o
COMMITvale como uma coisa só, e fica muito mais rápido. Você vai ver isso em Transações. - CREATE TABLE categorias (…);
- A estrutura da tabela, do jeito que foi escrita quando ela foi criada, com os espaços e tudo (veja CREATE TABLE).
- INSERT INTO categorias VALUES(…);
- Uma linha da tabela por
INSERT, com os valores na ordem das colunas. Os textos vêm em aspas simples, como manda o SQL. - COMMIT;
- Fecha a transação e grava tudo.
Rodar esse texto num banco vazio recria a tabela categorias exatamente como ela é. Essa é toda a ideia do dump.
2. O dump do banco inteiro num arquivo
Sem nome de tabela, o .dump escreve o banco inteiro. Em vez de jogar tudo na tela, a Ana manda para um arquivo, numa linha só do terminal:
$ sqlite3 loja.db .dump > backup.sql
$ ls -l loja.db backup.sql
-rw-rw-r-- 1 ana ana 30423 out 18 11:18 backup.sql
-rw-r--r-- 1 ana ana 45056 out 18 11:14 loja.db
$ head -4 backup.sql
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE categorias (
id INTEGER PRIMARY KEY,
$ tail -3 backup.sql
INSERT INTO itens_pedido VALUES(90,15,2,54.29999999999999716);
INSERT INTO itens_pedido VALUES(90,16,1,39.99000000000000198);
COMMIT;
$ grep -c '^INSERT' backup.sql
407 « 8+6+45+30+8+90+220: todas as linhas de todas as tabelas
- sqlite3 loja.db
- Abre o banco.
- .dump
- O comando de ponto, passado direto na linha de comando: o
sqlite3roda, escreve e sai. - > backup.sql
- O shell grava tudo o que sairia na tela no arquivo
backup.sql. O.sqlno fim do nome é só um costume, para lembrar que o arquivo é SQL.
O grep -c '^INSERT' conta as linhas que começam com INSERT: 407, exatamente o total de linhas das sete tabelas da loja. O dump ficou menor que o banco (30 KB contra 44 KB): é só texto, sem os índices nem o espaço livre que o arquivo do banco guarda por dentro. Num banco grande, ele também comprime muito bem com gzip.
Que números são esses: 54.29999999999999716?
No banco, o preço desse item é 54.3. O dump escreve o número com todas as casas que o computador guarda de verdade. Números com vírgula (o tipo REAL) são guardados em binário, e 54.3 não tem representação exata em binário, assim como 1/3 não tem em decimal (0,333…). O dump escreve a versão longa para que, ao restaurar, volte exatamente o mesmo número, sem nenhuma diferença:
SELECT preco_unitario FROM itens_pedido WHERE pedido_id = 90 AND produto_id = 15; SELECT 54.29999999999999716 = 54.3 AS igual;
O 1 quer dizer "verdadeiro": para o SQLite, os dois números são o mesmo. Mais sobre essas casas "feias" em Funções Numéricas.
3. Restaurar um dump
Para recriar o banco a partir da "receita", basta dar o arquivo de SQL para um sqlite3 com um banco novo. O < do shell faz o caminho contrário do >: lê o arquivo e entrega ao programa, como se você digitasse tudo:
$ sqlite3 restaurado.db < backup.sql $ ls -l restaurado.db -rw-r--r-- 1 ana ana 45056 out 18 11:18 restaurado.db $ sqlite3 restaurado.db "SELECT count(*) FROM produtos; SELECT count(*) FROM itens_pedido;" 45 220
O restaurado.db não existia: o sqlite3 criou o arquivo vazio, rodou os 407 INSERT e os sete CREATE TABLE, e pronto. As contagens batem com o original, e até o tamanho bateu: 45056 bytes, igual ao loja.db. Faz sentido, porque o loja.db da Ana foi criado exatamente assim, rodando um arquivo de SQL num banco vazio. O tamanho nem sempre bate (um banco que já teve muitos DELETE guarda espaço vazio que o dump não leva, como você vai ver em VACUUM e ANALYZE), e não precisa: o que importa é que os dados são os mesmos.
De dentro do sqlite3, o mesmo efeito vem do .read, que lê e roda um arquivo de SQL:
$ sqlite3 novo.db SQLite version 3.50.4 2025-07-30 19:33:53 Enter ".help" for usage hints. sqlite> .read backup.sql sqlite> SELECT count(*) FROM clientes; 30 sqlite> .quit
Como o dump é só texto com SQL, você pode abrir no editor de texto, procurar um cliente com grep, comparar dois backups com diff para ver o que mudou, ou guardar no Git. Com poucos ajustes (os tipos e o PRAGMA do começo), ele serve até para levar os dados para outro banco, como o PostgreSQL.
E é SQL de verdade: o editor do guia roda um trecho de dump. Aqui, uma tabela que não existe na loja, cupons, escrita no formato do dump:
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE cupons (codigo TEXT PRIMARY KEY, desconto REAL);
INSERT INTO cupons VALUES('AURORA10',0.1);
INSERT INTO cupons VALUES('FRETEGRATIS',0.0);
COMMIT;
SELECT * FROM cupons;
4. O .backup: a cópia exata
O "congelador" é o .backup. Dentro do sqlite3, ele grava uma cópia idêntica do banco aberto em outro arquivo:
$ sqlite3 loja.db SQLite version 3.50.4 2025-07-30 19:33:53 Enter ".help" for usage hints. sqlite> .backup copia.db sqlite> SELECT count(*) FROM clientes; 30
- .backup
- Copia o banco aberto, página por página, usando a "API de backup" do SQLite: enquanto copia, nenhum outro programa consegue deixar o banco pela metade.
- copia.db
- O arquivo de destino. Se já existir, é substituído sem perguntar.
Logo depois, um acidente: a Ana apaga a cliente 30 sem querer. Como ela tinha acabado de fazer a cópia, o .restore traz tudo de volta:
sqlite> DELETE FROM clientes WHERE id = 30; sqlite> SELECT count(*) FROM clientes; 29 sqlite> .restore copia.db sqlite> SELECT count(*) FROM clientes; 30 sqlite> SELECT nome FROM clientes WHERE id = 30; Rosângela Dias sqlite> .quit
- .restore copia.db
- O caminho contrário: substitui o banco aberto pelo conteúdo de
copia.db. Tudo o que o banco tinha antes do.restoresome, inclusive o que mudou depois do backup.
A Rosângela voltou. Fora do sqlite3, os dois arquivos têm o mesmo tamanho, e o PRAGMA integrity_check ("conferência de integridade") confirma que a cópia está saudável:
$ ls -l loja.db copia.db -rw-r--r-- 1 ana ana 45056 out 18 11:18 copia.db -rw-r--r-- 1 ana ana 45056 out 18 11:19 loja.db $ sqlite3 copia.db "PRAGMA integrity_check;" ok
Um backup que nunca foi conferido é uma esperança, não um backup. De tempos em tempos, abra a cópia e rode o integrity_check e uma ou duas contagens.
Um backup por dia, com a data no nome
O .backup também funciona numa linha só do terminal. Com o $(date +%F) do shell, que vira a data de hoje no formato ano-mês-dia, cada dia ganha o seu arquivo:
$ sqlite3 loja.db ".backup backup-$(date +%F).db" $ ls -l backup-* -rw-r--r-- 1 ana ana 45056 out 18 11:19 backup-2026-10-18.db
As aspas em volta de .backup … fazem o sqlite3 receber o comando inteiro como uma coisa só. Essa linha pode ir para um agendador de tarefas (como o cron, no Linux) e rodar sozinha toda noite.
5. VACUUM INTO: a cópia com SQL
O .backup é um comando de ponto, que só o programa sqlite3 entende. Um programa em Python ou PHP que usa o SQLite precisa de um jeito em SQL. É o VACUUM INTO ("aspire para dentro de"):
$ sqlite3 loja.db "VACUUM INTO 'compacta.db';" $ ls -l compacta.db -rw-r--r-- 1 ana ana 45056 out 18 11:19 compacta.db
- VACUUM
- O comando que reorganiza o banco por dentro e joga fora o espaço vazio (aula VACUUM e ANALYZE).
- INTO 'compacta.db'
- Em vez de reorganizar o próprio banco, grava o resultado num arquivo novo. O original não é tocado. O nome do arquivo vai entre aspas simples, como um texto.
A cópia sai já "aspirada", sem espaço desperdiçado. Aqui ela ficou do mesmo tamanho do original porque o loja.db está arrumado; num banco que já teve muitos DELETE, a cópia sai menor. Diferente do .backup, o VACUUM INTO não substitui um arquivo que já existe (veja em Quando dá errado).
O VACUUM INTO 'copia.db'; roda no editor e responde "VACUUM: ok". Mas o banco do editor vive na memória do seu navegador, e o arquivo também: não existe a sua pasta pessoal ali, e a cópia some junto com o banco. Backup de verdade, só no terminal.
6. Um backup dentro do próprio banco
Antes de uma mudança arriscada numa tabela só, como um aumento de preços, existe um truque rápido: copiar a tabela para outra tabela no mesmo banco, com CREATE TABLE … AS SELECT (veja Criar Tabela a partir de Consulta). Não substitui o backup em outro arquivo (se o notebook quebrar, as duas tabelas vão junto), mas salva de um UPDATE errado.
Experimente: a Ana guardou os produtos em produtos_backup e logo depois zerou todos os preços por engano. Escreva, no lugar indicado, um UPDATE que devolva a cada produto o preço que está na cópia (dica: SET preco = (SELECT b.preco FROM produtos_backup AS b WHERE b.id = produtos.id)).
CREATE TABLE produtos_backup AS SELECT * FROM produtos; UPDATE produtos SET preco = 0; -- o acidente -- escreva aqui o UPDATE que traz os preços de volta SELECT nome, preco FROM produtos WHERE id IN (1, 2, 3);
CREATE TABLE produtos_backup AS SELECT * FROM produtos; UPDATE produtos SET preco = 0; -- o acidente UPDATE produtos SET preco = (SELECT b.preco FROM produtos_backup AS b WHERE b.id = produtos.id); SELECT nome, preco FROM produtos WHERE id IN (1, 2, 3);
7. Formas diferentes de fazer
| Forma | O que gera | Vantagens | Cuidados |
|---|---|---|---|
sqlite3 loja.db .dump > backup.sql | Texto com SQL | Legível, comparável com diff, serve para outros bancos, comprime bem | Restaurar é rodar todo o SQL: num banco grande, demora |
.dump tabela | SQL de uma tabela | Leva uma tabela só para outro banco | Não leva as tabelas relacionadas |
.backup copia.db | Cópia exata do .db | Rápido, consistente, pronto para abrir | Arquivo binário: não dá para ler nem comparar no editor de texto |
.restore copia.db | (volta a cópia) | Desfaz tudo desde o backup | Apaga o que veio depois; com nome errado, apaga tudo |
VACUUM INTO 'copia.db' | Cópia compactada do .db | É SQL: funciona em qualquer programa que usa SQLite | Não sobrescreve um arquivo que já existe |
cp loja.db copia.db | Cópia do arquivo | Simples | Só com o banco fechado; esquece o -wal |
CREATE TABLE x AS SELECT … | Tabela de reserva no mesmo banco | Instantâneo, antes de uma mudança arriscada | Não é backup: fica no mesmo arquivo |
Qual usar? Para o dia a dia, um .backup com a data no nome, todo dia, guardado em outro lugar (um pendrive, outro computador, a nuvem). De vez em quando, um .dump, que você consegue ler mesmo daqui a dez anos, com qualquer programa.
Quando dá errado
A Ana tenta "restaurar" o backup.sql no próprio loja.db, que ainda tem todas as tabelas:
$ sqlite3 loja.db < backup.sql 2>&1 | head -6
Parse error near line 3: table categorias already exists
CREATE TABLE categorias ( id INTEGER PRIMARY KEY, nome TEX
^--- error here
Runtime error near line 8: UNIQUE constraint failed: categorias.id (19)
Runtime error near line 9: UNIQUE constraint failed: categorias.id (19)
Runtime error near line 10: UNIQUE constraint failed: categorias.id (19)
$ sqlite3 loja.db < backup.sql 2>&1 | wc -l
428
O que aconteceu: "erro perto da linha 3: a tabela categorias já existe", com a seta apontando onde o SQLite parou de entender. Depois, um "a restrição UNIQUE falhou em categorias.id" para cada INSERT, porque cada linha já estava lá (o (19) é o número do código de erro do SQLite para restrição violada). Ao todo, 428 linhas de erro. O 2>&1 | head -6 é só para mostrar as primeiras: junta as mensagens de erro na saída e corta nas 6 primeiras linhas. O banco não estragou (nada entrou duplicado), mas também não foi restaurado.
Como consertar: restaure um dump sempre num banco novo (sqlite3 restaurado.db < backup.sql), confira, e só então troque um arquivo pelo outro.
No editor acontece o mesmo: a Loja Aurora já está carregada, então o CREATE TABLE do dump bate na tabela que existe:
CREATE TABLE categorias (
id INTEGER PRIMARY KEY,
nome TEXT NOT NULL,
descricao TEXT
);
.restore de um arquivo que não existe: o banco some
$ sqlite3 loja.db SQLite version 3.50.4 2025-07-30 19:33:53 Enter ".help" for usage hints. sqlite> .restore nao-existe.db sqlite> .tables sqlite> SELECT count(*) FROM clientes; Parse error: no such table: clientes
O que aconteceu: este é o erro mais perigoso da aula, porque não aparece mensagem nenhuma. O .restore tentou abrir nao-existe.db; como o arquivo não existia, o SQLite criou um banco novo, vazio, com esse nome, e restaurou esse vazio por cima do loja.db. O .tables não mostra nada e "não existe a tabela clientes". Se a Ana não tivesse outra cópia, a loja teria perdido tudo.
Como consertar: aqui havia o copia.db, então deu para voltar:
sqlite> .restore copia.db sqlite> SELECT count(*) FROM clientes; 30 sqlite> .quit $ ls -l loja.db nao-existe.db -rw-r--r-- 1 ana ana 45056 out 18 11:19 loja.db -rw-r--r-- 1 ana ana 0 out 18 11:19 nao-existe.db $ rm nao-existe.db
Repare no nao-existe.db com 0 bytes, que o .restore deixou para trás. Para nunca cair nessa: antes de um .restore, confira o nome com ls e faça um .backup do banco atual (por exemplo, .backup antes-do-restore.db). Assim, até um .restore errado tem volta.
VACUUM INTO num arquivo que já existe
$ sqlite3 loja.db "VACUUM INTO 'compacta.db';" Error: stepping, output file already exists
O que aconteceu: "o arquivo de saída já existe" (o stepping diz que o erro veio ao executar o comando). Ao contrário do .backup, o VACUUM INTO se recusa a apagar um arquivo, e isso é uma proteção. Dentro do sqlite3, a mesma situação aparece como Runtime error: output file already exists.
Como consertar: use outro nome (a data no nome resolve) ou apague o arquivo antigo antes, se tiver certeza.
sqlite> .backup pasta-que-nao-existe/copia.db Error: cannot open "pasta-que-nao-existe/copia.db"
O que aconteceu: "não consigo abrir". O sqlite3 cria o arquivo, mas não cria pastas.
Como consertar: crie a pasta antes, no terminal: mkdir -p ~/backups, e depois .backup ~/backups/copia.db.
$ sqlite3 backup.sql ".tables" Error: file is not a database
O que aconteceu: "o arquivo não é um banco de dados". O backup.sql é texto, a receita; o sqlite3 esperava um arquivo de banco, o bolo pronto.
Como consertar: um dump se roda num banco novo: sqlite3 novo.db < backup.sql. Só o que o .backup e o VACUUM INTO geram se abre direto.
Nos outros bancos
A ideia da "receita" existe em todos: um programa que escreve o banco como SQL. Os trechos são ilustrativos (não rodam no SQLite), conforme a documentação oficial de cada banco:
mysqldump loja > backup.sql # MySQL: o "dump" do banco loja mysql loja < backup.sql # MySQL: restaurar (o banco loja precisa existir) pg_dump loja > backup.sql # PostgreSQL: o "dump" em SQL psql -X loja < backup.sql # PostgreSQL: restaurar num banco já criado
-- SQL Server: o backup é um comando SQL, gravado pelo servidor BACKUP DATABASE loja TO DISK = 'C:\backups\loja.bak'; RESTORE DATABASE loja FROM DISK = 'C:\backups\loja.bak';
Nos bancos com servidor, o banco não é um arquivo que você possa simplesmente copiar: são vários arquivos, em uso o tempo todo pelo servidor. Por isso as ferramentas próprias são o único jeito seguro.
Referência rápida
| Comando | O que faz |
|---|---|
.dump | Escreve o banco inteiro como SQL |
.dump tabela | Escreve só uma tabela |
sqlite3 loja.db .dump > backup.sql | Salva o dump num arquivo |
sqlite3 novo.db < backup.sql | Recria o banco a partir do dump |
.read backup.sql | Roda um arquivo de SQL de dentro do sqlite3 |
.backup copia.db | Cópia exata e consistente do banco aberto (substitui o destino) |
.restore copia.db | Substitui o banco aberto pela cópia |
VACUUM INTO 'copia.db'; | Cópia compactada, em SQL (não substitui arquivo existente) |
PRAGMA integrity_check; | Confere se o banco (ou a cópia) está saudável: responde ok |
| Mensagem | Quer dizer |
|---|---|
Parse error near line 3: table categorias already exists | Dump restaurado num banco que já tem as tabelas |
Runtime error near line 8: UNIQUE constraint failed: categorias.id (19) | A linha já existe no banco |
Error: stepping, output file already exists | VACUUM INTO num arquivo que já existe |
Error: cannot open "…" | Pasta ou arquivo inexistente |
Error: file is not a database | Tentou abrir um arquivo de texto (um dump) como banco |
(nenhuma mensagem) depois de .restore | Confira com .tables: um nome errado restaura um banco vazio |
Exercícios
1. O dump num arquivo
Complete a linha do terminal que salva o banco inteiro como SQL no arquivo backup.sql:
2. A cópia exata
Dentro do sqlite3, complete o comando que grava uma cópia do banco aberto em copia.db, e o que traz essa cópia de volta:
. copia.db
3. O que tem dentro do arquivo gerado por sqlite3 loja.db .dump > backup.sql?
4. A Ana digitou .restore copai.db (com o nome errado). O que acontece?
5. Por que o .backup é melhor que o cp loja.db copia.db?
6. O sqlite3 loja.db < backup.sql respondeu table categorias already exists. O que a Ana deveria ter feito?
Missão final: a tabela que sumiu
Missão: alguém apagou a tabela categorias inteira. Por sorte, a Ana tem o .dump categorias da seção 1. Cole o dump no lugar indicado (do PRAGMA ao COMMIT) e rode: a contagem do fim tem de dar 8.
PRAGMA foreign_keys = OFF; DROP TABLE categorias; -- o acidente -- cole aqui o dump da tabela categorias SELECT count(*) AS categorias FROM categorias;
PRAGMA foreign_keys = OFF;
DROP TABLE categorias; -- o acidente
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE categorias (
id INTEGER PRIMARY KEY,
nome TEXT NOT NULL,
descricao TEXT
);
INSERT INTO categorias VALUES(1,'Papelaria','Material escolar e de escritório');
INSERT INTO categorias VALUES(2,'Eletrônicos','Acessórios e dispositivos eletrônicos');
INSERT INTO categorias VALUES(3,'Casa e Cozinha','Utensílios para o lar');
INSERT INTO categorias VALUES(4,'Livros','Livros, dicionários e guias');
INSERT INTO categorias VALUES(5,'Brinquedos','Brinquedos e jogos para todas as idades');
INSERT INTO categorias VALUES(6,'Esporte e Lazer','Artigos esportivos e de lazer');
INSERT INTO categorias VALUES(7,'Alimentos','Alimentos não perecíveis e regionais');
INSERT INTO categorias VALUES(8,'Jardim','Ferramentas e artigos de jardinagem');
COMMIT;
SELECT count(*) AS categorias FROM categorias;
Rode primeiro o código como está: o SELECT do fim dá no such table: categorias, porque a tabela foi apagada. Depois de colar o dump, as 8 categorias voltam. Por que o PRAGMA foreign_keys = OFF no começo? Com ele ligado, o SQLite não deixa apagar uma tabela que outras tabelas usam (os produtos apontam para as categorias), e o "acidente" nem aconteceria. Você vai ver esse pragma em PRAGMAs Úteis.
Resumo
- O banco SQLite é um arquivo só: perder o arquivo é perder tudo. Faça backup e guarde em outro lugar.
.dumpescreve o banco como SQL (CREATE TABLE+INSERT, numa transação).sqlite3 loja.db .dump > backup.sqlsalva;sqlite3 novo.db < backup.sqlrestaura, sempre num banco novo..backup copia.dbgrava uma cópia exata e consistente;.restore copia.dbtroca o banco aberto pela cópia. Os dois substituem o destino sem perguntar.VACUUM INTO 'copia.db'é a cópia em SQL, já compactada, e não sobrescreve arquivo existente.cpsó com o banco fechado. Confira os backups comPRAGMA integrity_check.- Cuidado com o
.restorede um nome errado: ele restaura um banco vazio, sem aviso. Faça um.backupantes de qualquer.restore.