SQLite com Python: banco de dados sem instalar nada
Crie e manipule bancos de dados locais com SQLite e o módulo sqlite3 que já vem com Python.
O que é o SQLite?
O SQLite é um banco de dados relacional que funciona dentro de um único arquivo. Diferente do PostgreSQL ou MySQL, ele não precisa de um servidor rodando — o banco inteiro fica em um arquivo .db no seu computador. O Python já traz o módulo sqlite3 na biblioteca padrão, então basta importar e usar.
Quando usar (e quando não usar)
- Use para protótipos, apps desktop/mobile, testes automatizados e projetos pessoais.
- Não use para aplicações web com muitos acessos simultâneos, sistemas com alta disponibilidade ou bancos muito grandes (acima de alguns GB).
Nesses casos, migre para PostgreSQL ou MySQL. A boa notícia é que o SQL é praticamente o mesmo.
Conectando ao banco
import sqlite3
conexao = sqlite3.connect("tarefas.db")
cursor = conexao.cursor()
Se o arquivo não existir, o SQLite cria automaticamente. O cursor é o objeto que executa comandos SQL. Para criar um banco em memória (útil para testes), use sqlite3.connect(":memory:").
Criando tabelas
cursor.execute("""
CREATE TABLE IF NOT EXISTS tarefas (
id INTEGER PRIMARY KEY AUTOINCREMENT,
titulo TEXT NOT NULL,
descricao TEXT,
prioridade TEXT DEFAULT 'media',
concluida INTEGER DEFAULT 0,
criada_em TEXT DEFAULT CURRENT_TIMESTAMP
)
""")
conexao.commit()
O IF NOT EXISTS evita erros caso a tabela já exista. O commit() salva as alterações — sem ele, nada é persistido.
| Tipo | Descrição | Exemplo |
|---|---|---|
| INTEGER | Números inteiros | 1, 42, -7 |
| REAL | Números decimais | 3.14, 99.90 |
| TEXT | Texto (strings) | "Python", "Ola" |
| BLOB | Dados binários | Imagens, arquivos |
| NULL | Valor ausente | None |
INSERT: inserindo dados
cursor.execute(
"INSERT INTO tarefas (titulo, descricao, prioridade) VALUES (?, ?, ?)",
("Estudar Python", "Completar o capitulo sobre SQLite", "alta")
)
conexao.commit()
# Inserir varias de uma vez
tarefas = [
("Comprar cafe", None, "baixa"),
("Revisar codigo", "Pull request do projeto X", "alta"),
("Responder emails", None, "media"),
]
cursor.executemany(
"INSERT INTO tarefas (titulo, descricao, prioridade) VALUES (?, ?, ?)",
tarefas
)
conexao.commit()
NUNCA faça isso
Concatenar strings em queries é uma falha de segurança grave chamada SQL injection:
# ERRADO - vulneravel a SQL injection!
titulo = "Estudar Python"
cursor.execute(f"INSERT INTO tarefas (titulo) VALUES ('{titulo}')")
Um usuário malicioso poderia enviar '); DROP TABLE tarefas; -- como título e destruir seu banco. Sempre use placeholders (?).
SELECT: consultando dados
# Todos os registros
cursor.execute("SELECT id, titulo, prioridade FROM tarefas")
for tarefa in cursor.fetchall():
print(f"[{tarefa[0]}] {tarefa[1]} (prioridade: {tarefa[2]})")
# Filtrar com WHERE
cursor.execute("SELECT titulo FROM tarefas WHERE prioridade = ?", ("alta",))
# Busca parcial com LIKE
cursor.execute("SELECT titulo FROM tarefas WHERE titulo LIKE ?", ("%Python%",))
# Buscar um unico registro
cursor.execute("SELECT * FROM tarefas WHERE id = ?", (1,))
tarefa = cursor.fetchone()
if tarefa:
print(f"Encontrada: {tarefa[1]}")
Para retornar dicionários em vez de tuplas, configure o row_factory:
conexao.row_factory = sqlite3.Row
cursor = conexao.cursor()
cursor.execute("SELECT * FROM tarefas WHERE prioridade = ?", ("alta",))
for tarefa in cursor.fetchall():
print(f"{tarefa['titulo']} - {tarefa['prioridade']}")
Ordenar e limitar
cursor.execute("SELECT titulo FROM tarefas ORDER BY criada_em DESC LIMIT 3")
# Paginacao: pular 3, pegar as proximas 3
cursor.execute("SELECT titulo FROM tarefas LIMIT 3 OFFSET 3")
UPDATE: atualizando dados
cursor.execute(
"UPDATE tarefas SET concluida = 1 WHERE id = ?",
(1,)
)
conexao.commit()
print(f"{cursor.rowcount} tarefa(s) atualizada(s)")
DELETE: removendo dados
cursor.execute("DELETE FROM tarefas WHERE id = ?", (5,))
conexao.commit()
# Remover todas as concluidas
cursor.execute("DELETE FROM tarefas WHERE concluida = 1")
conexao.commit()
Cuidado: DELETE FROM tarefas sem WHERE apaga tudo. Sempre use filtros.
Context managers: segurança automática
Use o with para garantir que tudo seja salvo e fechado corretamente:
import sqlite3
with sqlite3.connect("tarefas.db") as conexao:
cursor = conexao.cursor()
cursor.execute(
"INSERT INTO tarefas (titulo, prioridade) VALUES (?, ?)",
("Nova tarefa", "media")
)
# commit automatico ao sair do bloco sem erros
# rollback automatico se houver excecao
Projeto prático: agenda de contatos
Vamos construir uma agenda no terminal com todas as operações CRUD:
import sqlite3
def criar_tabela(cursor):
cursor.execute("""
CREATE TABLE IF NOT EXISTS contatos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nome TEXT NOT NULL,
telefone TEXT,
email TEXT UNIQUE
)
""")
def adicionar(cursor):
nome = input("Nome: ")
telefone = input("Telefone: ")
email = input("Email: ")
try:
cursor.execute(
"INSERT INTO contatos (nome, telefone, email) VALUES (?, ?, ?)",
(nome, telefone, email)
)
print(f"Contato '{nome}' adicionado!")
except sqlite3.IntegrityError:
print("Erro: ja existe um contato com esse email.")
def listar(cursor):
cursor.execute("SELECT id, nome, telefone, email FROM contatos ORDER BY nome")
contatos = cursor.fetchall()
if not contatos:
print("Nenhum contato cadastrado.")
return
print(f"\n{'ID':<5} {'Nome':<20} {'Telefone':<15} {'Email':<25}")
print("-" * 65)
for c in contatos:
print(f"{c[0]:<5} {c[1]:<20} {c[2] or '-':<15} {c[3] or '-':<25}")
def buscar(cursor):
termo = input("Buscar por nome: ")
cursor.execute(
"SELECT id, nome, telefone, email FROM contatos WHERE nome LIKE ?",
(f"%{termo}%",)
)
for c in cursor.fetchall():
print(f"[{c[0]}] {c[1]} | {c[2] or '-'} | {c[3] or '-'}")
def remover(cursor):
id_contato = input("ID do contato para remover: ")
cursor.execute("DELETE FROM contatos WHERE id = ?", (id_contato,))
print("Removido." if cursor.rowcount > 0 else "Nao encontrado.")
def main():
with sqlite3.connect("agenda.db") as conexao:
cursor = conexao.cursor()
criar_tabela(cursor)
while True:
print("\n=== Agenda de Contatos ===")
print("1. Adicionar 2. Listar 3. Buscar 4. Remover 0. Sair")
opcao = input("Opcao: ")
acoes = {"1": adicionar, "2": listar, "3": buscar, "4": remover}
if opcao == "0":
print("Ate logo!")
break
elif opcao in acoes:
acoes[opcao](cursor)
else:
print("Opcao invalida.")
if __name__ == "__main__":
main()
SQLite vs bancos "de verdade"
| Característica | SQLite | PostgreSQL / MySQL |
|---|---|---|
| Instalação | Nenhuma (embutido) | Servidor separado |
| Concorrência | Uma escrita por vez | Múltiplas escritas |
| Rede | Apenas local | Acesso remoto |
| Tamanho | Ideal até poucos GB | Terabytes sem problema |
| Usuários | Um por vez (geralmente) | Milhares simultâneos |
A regra prática: comece com SQLite para desenvolver e testar. Quando for para produção com múltiplos usuários, migre para PostgreSQL. Como o SQL é o mesmo, a migração costuma ser simples.
Próximos passos
Com o SQLite você já consegue criar sistemas completos de armazenamento local. O próximo passo natural é aprender SQL com mais profundidade (JOINs, agregações, subconsultas) e depois explorar ORMs como o SQLAlchemy, que permitem trabalhar com bancos de dados usando classes Python em vez de SQL puro.