Resposta rápida
O Python já traz o módulo sqlite3, que cria e acessa bancos SQLite (um arquivo .db) sem instalar nada. Conecte com sqlite3.connect("banco.db"), rode SQL com execute() usando placeholders ? para os valores e grave com commit().
1
2
3
4
5
6
import sqlite3
conexao = sqlite3.connect(":memory:")
conexao.execute("CREATE TABLE alunos (nome TEXT, nota REAL)")
conexao.execute("INSERT INTO alunos VALUES (?, ?)", ("Ana", 9.5))
print(conexao.execute("SELECT * FROM alunos").fetchall()) # [('Ana', 9.5)]
Resumo em 30 segundos:
-
sqlite3é da biblioteca padrão: bastaimport sqlite3. - Use sempre
?(ou:nome) para valores. Nunca f-string: isso abre brecha para SQL injection. -
INSERT,UPDATEeDELETEsó ficam gravados apóscommit(). -
with conexao:faz commit ou rollback, mas não fecha a conexão. -
conexao.row_factory = sqlite3.Rowpermitelinha["nome"]edict(linha).
Salve salve Pythonista!
Chega uma hora em que guardar dados em listas, dicionários ou arquivos de texto não basta: você precisa buscar, filtrar, atualizar e garantir que nada se perca no meio do caminho. Para isso existe o banco de dados, e o jeito mais simples de começar em Python é o SQLite.
Neste guia você vai aprender a conectar, criar tabelas, inserir dados com segurança (e ver na prática o estrago que uma f-string pode causar), consultar com fetchone() e fetchall(), receber linhas como dicionário, atualizar, apagar e controlar transações com commit(), rollback() e with. No final há erros comuns com a mensagem real e exercícios resolvidos.
Todos os exemplos foram executados no Python 3.13 (com a biblioteca SQLite 3.50.4 embutida). Onde o comportamento muda entre versões, o texto avisa.
Então… Bora pro post! ![]()
Vá Direto ao Assunto…
- O que é o SQLite e o módulo sqlite3
- Conectar ao banco e criar uma tabela
- Inserir dados com placeholders (?)
- Inserir várias linhas com executemany
- Consultar dados: fetchone, fetchall e fetchmany
- Linhas como dicionário com row_factory = sqlite3.Row
- UPDATE e DELETE
- commit, rollback e o context manager da conexão
- Banco de dados em memória (:memory:)
- Quando usar SQLite, arquivo ou outro banco
- Erros comuns
- Exercícios resolvidos
O que é o SQLite e o módulo sqlite3
O que é o SQLite? SQLite é um banco de dados relacional que guarda tudo em um único arquivo no disco, sem servidor, usuário ou senha. O Python acessa esse banco pelo módulo
sqlite3, que faz parte da biblioteca padrão. Ele entende SQL comum (CREATE TABLE,INSERT,SELECT,UPDATE,DELETE) e é ideal para scripts, aplicações desktop, protótipos, testes e sites com pouco tráfego de escrita.
O SQLite está em todo lugar: navegadores, celulares e aplicativos usam para guardar dados locais. O Django, inclusive, cria um arquivo db.sqlite3 como banco padrão de todo projeto novo, e é nele que o comando migrate do Django cria as tabelas.
Como o módulo já vem com o Python, não há nada a instalar. Para ver a versão da biblioteca SQLite embutida no seu Python:
1
2
3
import sqlite3
print(sqlite3.sqlite_version)
1
3.50.4
A referência completa está na documentação oficial do sqlite3.
Conectar ao banco e criar uma tabela
O fluxo de trabalho tem sempre os mesmos passos: conectar, obter um cursor, executar SQL, confirmar (commit) e fechar.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sqlite3
conexao = sqlite3.connect("loja.db")
cursor = conexao.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS produtos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nome TEXT NOT NULL UNIQUE,
preco REAL NOT NULL,
estoque INTEGER DEFAULT 0
)
""")
conexao.commit()
O que acontece aqui:
-
sqlite3.connect("loja.db")abre o arquivoloja.db, criando-o se ele não existir. O caminho segue as mesmas regras de qualquer arquivo em Python (relativo à pasta onde o script roda), como vimos em como manipular arquivos utilizando Python. - O cursor é o objeto que executa comandos e percorre resultados. A conexão também tem um atalho
conexao.execute(), que cria o cursor para você. -
IF NOT EXISTSevita erro ao rodar o script de novo. -
INTEGER PRIMARY KEYgera oidautomaticamente;UNIQUEimpede dois produtos com o mesmo nome.
Os tipos do SQLite são poucos: INTEGER, REAL (número com casas decimais), TEXT, BLOB (bytes) e NULL. Eles viram int, float, str, bytes e None no Python.
Inserir dados com placeholders (?)
Para inserir valores, escreva ? no lugar de cada valor e passe os dados em uma tupla no segundo argumento do execute():
1
2
3
4
5
6
cursor.execute(
"INSERT INTO produtos (nome, preco, estoque) VALUES (?, ?, ?)",
("Teclado", 199.9, 15),
)
conexao.commit()
print(cursor.lastrowid, cursor.rowcount)
1
1 1
cursor.lastrowid traz o id gerado para a linha inserida, e cursor.rowcount diz quantas linhas o comando afetou.
Também existe o estilo nomeado, com :nome no SQL e um dicionário nos dados. É ótimo quando os valores já estão em um dict:
1
2
3
4
5
6
7
produto = {"nome": "Mouse", "preco": 89.9, "estoque": 40}
cursor.execute(
"INSERT INTO produtos (nome, preco, estoque) VALUES (:nome, :preco, :estoque)",
produto,
)
conexao.commit()
print(cursor.lastrowid)
1
2
Não misture os estilos: a partir do Python 3.14, passar uma tupla para placeholders nomeados gera sqlite3.ProgrammingError (no 3.12 e 3.13 era só um aviso de depreciação).
SQL injection: o perigo de montar SQL com f-string
É tentador escrever f"SELECT ... WHERE nome = '{nome}'". Não faça isso com valores que vêm de fora (formulário, input(), API, arquivo). Veja um login montado com f-string, em um banco de teste separado (teste) para não misturar com a loja:
1
2
3
4
5
6
7
8
9
10
11
12
import sqlite3
teste = sqlite3.connect(":memory:")
teste.execute("CREATE TABLE usuarios (id INTEGER PRIMARY KEY, nome TEXT, senha TEXT)")
teste.execute("INSERT INTO usuarios (nome, senha) VALUES ('admin', 'Adm!n2026')")
nome = "admin' --" # digitado pelo "usuário"
senha = "qualquer"
sql = f"SELECT nome FROM usuarios WHERE nome = '{nome}' AND senha = '{senha}'"
print(sql)
print(teste.execute(sql).fetchall())
1
2
SELECT nome FROM usuarios WHERE nome = 'admin' --' AND senha = 'qualquer'
[('admin',)]
O apóstrofo digitado fechou a string e o -- transformou o resto da consulta em comentário SQL. A verificação de senha simplesmente sumiu, e o atacante entrou como admin sem saber a senha. Isso é SQL injection, uma das falhas de segurança mais antigas e ainda mais exploradas da web.
Agora a mesma consulta com placeholders:
1
2
sql = "SELECT nome FROM usuarios WHERE nome = ? AND senha = ?"
print(teste.execute(sql, (nome, senha)).fetchall())
1
[]
Com ?, o valor é enviado separado do comando e tratado sempre como dado: o banco procurou um usuário chamado literalmente admin' --, que não existe. A documentação do sqlite3 é explícita: não use operações de string do Python para montar consultas.
Um detalhe: o execute() recusa mais de um comando por chamada (sqlite3.ProgrammingError: You can only execute one statement at a time.), o que barra o clássico '; DROP TABLE usuarios; --. Mas, como o exemplo mostrou, não é preciso um segundo comando para causar estrago. A única defesa confiável são os placeholders.
Placeholders servem para valores. Nomes de tabela e de coluna não podem ser ? (SELECT * FROM ? dá erro de sintaxe). Se precisar de um nome dinâmico, valide-o contra uma lista fixa de nomes permitidos antes de montar o SQL.
Inserir várias linhas com executemany
Para inserir uma lista de registros, use executemany(): ele executa o mesmo comando uma vez para cada tupla da lista.
1
2
3
4
5
6
7
8
9
novos = [
("Monitor", 1299.0, 5),
("Headset", 349.9, 12),
("Webcam", 259.0, 0),
("Cabo HDMI", 39.9, 100),
]
cursor.executemany("INSERT INTO produtos (nome, preco, estoque) VALUES (?, ?, ?)", novos)
conexao.commit()
print(cursor.rowcount)
1
4
Além de mais curto que um for com execute(), é mais rápido, porque o comando é preparado uma vez só. Se a lista vier de uma lista de dicionários, use placeholders nomeados e passe a lista de dicts direto.
Está curtindo esse conteúdo? ![]()
Que tal receber 30 dias de conteúdo direto na sua Caixa de Entrada?
Consultar dados: fetchone, fetchall e fetchmany
Depois de um SELECT, o cursor guarda o resultado e você escolhe como ler:
1
2
3
cursor.execute("SELECT id, nome, preco FROM produtos WHERE preco > ? ORDER BY preco", (100,))
for linha in cursor.fetchall():
print(linha)
1
2
3
4
(1, 'Teclado', 199.9)
(5, 'Webcam', 259.0)
(4, 'Headset', 349.9)
(3, 'Monitor', 1299.0)
Repare no (100,): mesmo com um único valor, os parâmetros precisam ser uma tupla, e tupla de um elemento exige a vírgula.
Para buscar um registro só, use fetchone(). Ele devolve uma tupla, ou None se nada for encontrado:
1
2
3
4
5
6
7
8
9
cursor.execute("SELECT nome, preco FROM produtos WHERE id = ?", (1,))
print(cursor.fetchone())
cursor.execute("SELECT nome, preco FROM produtos WHERE id = ?", (999,))
print(cursor.fetchone())
cursor.execute("SELECT COUNT(*), MAX(preco) FROM produtos")
total, mais_caro = cursor.fetchone()
print(total, mais_caro)
1
2
3
('Teclado', 199.9)
None
6 1299.0
Para resultados grandes, fetchmany(n) lê em lotes, e iterar o cursor direto lê uma linha por vez sem montar a lista inteira na memória:
1
2
3
4
5
6
cursor.execute("SELECT nome FROM produtos ORDER BY id")
print(cursor.fetchmany(2))
print(cursor.fetchmany(2))
for (nome,) in conexao.execute("SELECT nome FROM produtos WHERE estoque >= ?", (40,)):
print(nome)
1
2
3
4
[('Teclado',), ('Mouse',)]
[('Monitor',), ('Headset',)]
Mouse
Cabo HDMI
| Método | Devolve | Quando usar |
|---|---|---|
fetchone() |
Uma tupla ou None
|
Buscar por id, contar, MAX, SUM, checar se existe |
fetchall() |
Lista com todas as linhas | Resultados pequenos que você vai usar inteiros |
fetchmany(n) |
Lista com até n linhas |
Processar resultados grandes em lotes |
for linha in cursor: |
Uma linha por volta | Percorrer resultados grandes sem carregar tudo |
Linhas como dicionário com row_factory = sqlite3.Row
Acessar linha[2] funciona, mas fica ilegível: qual coluna era a 2? Defina row_factory = sqlite3.Row na conexão e as linhas passam a aceitar acesso por nome:
1
2
3
4
5
6
7
8
9
10
11
12
conexao.row_factory = sqlite3.Row
cursor = conexao.cursor()
cursor.execute("SELECT * FROM produtos WHERE nome = ?", ("Mouse",))
produto = cursor.fetchone()
print(produto["nome"], produto["preco"], produto[0])
print(produto.keys())
print(dict(produto))
sem_estoque = [dict(p) for p in conexao.execute("SELECT nome, estoque FROM produtos WHERE estoque = 0")]
print(sem_estoque)
1
2
3
4
Mouse 89.9 2
['id', 'nome', 'preco', 'estoque']
{'id': 2, 'nome': 'Mouse', 'preco': 89.9, 'estoque': 40}
[{'nome': 'Webcam', 'estoque': 0}]
O row_factory vale para os cursores criados depois da atribuição, por isso criamos um cursor novo na linha 2. Um sqlite3.Row aceita índice e nome (sem diferenciar maiúsculas), e dict(linha) o converte em um dicionário comum, pronto para virar JSON em uma API.
UPDATE e DELETE
Atualizar e remover seguem o mesmo padrão, sempre com WHERE e placeholders. O rowcount informa quantas linhas foram afetadas:
1
2
3
4
5
6
7
8
9
10
cursor.execute("UPDATE produtos SET preco = ROUND(preco * 0.9, 2) WHERE preco > ?", (1000,))
print("Atualizados:", cursor.rowcount)
cursor.execute("DELETE FROM produtos WHERE estoque = ?", (0,))
print("Removidos:", cursor.rowcount)
conexao.commit()
for p in conexao.execute("SELECT nome, preco FROM produtos ORDER BY id"):
print(tuple(p))
conexao.close()
1
2
3
4
5
6
7
Atualizados: 1
Removidos: 1
('Teclado', 199.9)
('Mouse', 89.9)
('Monitor', 1169.1)
('Headset', 349.9)
('Cabo HDMI', 39.9)
O Monitor ganhou 10% de desconto e a Webcam (estoque zero) foi removida. Duas dicas: um UPDATE ou DELETE sem WHERE afeta a tabela inteira, e um rowcount igual a 0 significa que nenhuma linha casou com o filtro (não é erro, então confira se precisar).
commit, rollback e o context manager da conexão
Uma transação agrupa comandos que precisam acontecer juntos: ou todos são gravados, ou nenhum. No modo padrão, o sqlite3 abre uma transação automaticamente antes de INSERT, UPDATE, DELETE e REPLACE, e ela só é gravada com commit(). Veja o que acontece quando o commit é esquecido:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import sqlite3
conexao = sqlite3.connect("banco.db")
conexao.execute("""
CREATE TABLE IF NOT EXISTS contas (
titular TEXT PRIMARY KEY,
saldo REAL NOT NULL CHECK (saldo >= 0)
)
""")
conexao.executemany("INSERT INTO contas VALUES (?, ?)", [("Ana", 500.0), ("Bruno", 100.0)])
conexao.commit()
conexao.execute("INSERT INTO contas VALUES (?, ?)", ("Carla", 50.0)) # sem commit
conexao.close()
conexao = sqlite3.connect("banco.db")
print(conexao.execute("SELECT titular FROM contas").fetchall())
conexao.close()
1
[('Ana',), ('Bruno',)]
A Carla sumiu: close() não faz commit, e a alteração pendente foi descartada.
O rollback() faz o contrário do commit: desfaz tudo o que foi feito desde o início da transação. O exemplo clássico é a transferência bancária. A regra CHECK (saldo >= 0) impede saldo negativo, então tirar R$ 600 da Ana (que tem 500) falha. Sem rollback, o Bruno ficaria com o dinheiro que nunca saiu da conta da Ana:
1
2
3
4
5
6
7
8
9
10
conexao = sqlite3.connect("banco.db")
try:
conexao.execute("UPDATE contas SET saldo = saldo + ? WHERE titular = ?", (600, "Bruno"))
conexao.execute("UPDATE contas SET saldo = saldo - ? WHERE titular = ?", (600, "Ana"))
conexao.commit()
except sqlite3.IntegrityError as erro:
conexao.rollback()
print("Transferência cancelada:", erro)
print(conexao.execute("SELECT * FROM contas").fetchall())
1
2
Transferência cancelada: CHECK constraint failed: saldo >= 0
[('Ana', 500.0), ('Bruno', 100.0)]
O crédito do Bruno (primeiro UPDATE) foi desfeito junto. Para revisar try/except, veja o nosso guia de tratamento de erros e exceções no Python.
with conexao: commit e rollback automáticos
Escrever try/commit/rollback toda vez cansa. A conexão funciona como context manager: se o bloco with terminar sem erro, ela faz commit; se sair com exceção, faz rollback (e a exceção continua subindo).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def transferir(conexao, origem, destino, valor):
with conexao: # commit se der certo, rollback se der erro
conexao.execute("UPDATE contas SET saldo = saldo + ? WHERE titular = ?", (valor, destino))
conexao.execute("UPDATE contas SET saldo = saldo - ? WHERE titular = ?", (valor, origem))
transferir(conexao, "Ana", "Bruno", 200)
print(conexao.execute("SELECT * FROM contas").fetchall())
try:
transferir(conexao, "Ana", "Bruno", 1000)
except sqlite3.IntegrityError as erro:
print("Erro:", erro)
print(conexao.execute("SELECT * FROM contas").fetchall())
conexao.close()
1
2
3
[('Ana', 300.0), ('Bruno', 300.0)]
Erro: CHECK constraint failed: saldo >= 0
[('Ana', 300.0), ('Bruno', 300.0)]
Atenção à pegadinha: o with conexao: não fecha a conexão, diferente do with open() de arquivos. Por isso ainda conseguimos consultar depois dos blocos. Para fechar automaticamente, combine com contextlib.closing():
1
2
3
4
5
6
from contextlib import closing
with closing(sqlite3.connect("banco.db")) as conexao:
with conexao:
conexao.execute("UPDATE contas SET saldo = saldo + 1 WHERE titular = ?", ("Ana",))
print(conexao.execute("SELECT saldo FROM contas WHERE titular = ?", ("Ana",)).fetchone())
1
(301.0,)
O with de fora fecha a conexão; o de dentro controla a transação. Fechar importa: desde o Python 3.13, uma conexão que nunca é fechada emite ResourceWarning.
Banco de dados em memória (:memory:)
Passando ":memory:" no lugar do nome do arquivo, o banco é criado na RAM. Ele é muito rápido e desaparece quando a conexão fecha:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sqlite3
memoria = sqlite3.connect(":memory:")
memoria.execute("CREATE TABLE cache (chave TEXT PRIMARY KEY, valor TEXT)")
memoria.execute("INSERT INTO cache VALUES (?, ?)", ("usuario:1", "Ana"))
print(memoria.execute("SELECT valor FROM cache WHERE chave = ?", ("usuario:1",)).fetchone())
memoria.close()
memoria = sqlite3.connect(":memory:")
try:
memoria.execute("SELECT * FROM cache")
except sqlite3.OperationalError as erro:
print(erro)
memoria.close()
1
2
('Ana',)
no such table: cache
A segunda conexão abriu um banco novo e vazio. Use :memory: em testes automatizados (cada teste começa com um banco limpo), para experimentar SQL e para processar dados temporários.
Saber escolher entre SQLite, um arquivo simples e um banco mais robusto vem de já ter apanhado num projeto real. Na Jornada Python esse aprendizado acontece na prática, com PostgreSQL incluso na grade:
Quando usar SQLite, arquivo ou outro banco
| Situação | Melhor escolha | Por quê |
|---|---|---|
| Script, app desktop, protótipo, dados locais | SQLite (sqlite3) |
Zero instalação, um único arquivo |
| Testes automatizados | SQLite com :memory:
|
Banco limpo e rápido a cada teste |
| Guardar configuração ou lista pequena | Arquivo JSON ou CSV | Não precisa de consultas nem transações |
| Site com muitos usuários escrevendo ao mesmo tempo | PostgreSQL ou MySQL | O SQLite permite um escritor por vez |
| Projeto Django | SQLite no desenvolvimento, PostgreSQL em produção | É o padrão do startproject; em produção, escala melhor |
Se o seu projeto crescer, veja como conectar o Django ao PostgreSQL.
Erros comuns
Estes são os erros que mais aparecem com quem começa a usar o sqlite3, com a última linha real do traceback.
Passar uma string em vez de tupla (ProgrammingError)
1
cursor.execute("INSERT INTO clientes (nome) VALUES (?)", ("Maria"))
1
sqlite3.ProgrammingError: Incorrect number of bindings supplied. The current statement uses 1, and there are 5 supplied.
("Maria") é só a string "Maria" entre parênteses, e cada uma das 5 letras virou um parâmetro. Correção: tupla de um elemento leva vírgula, ("Maria",).
Consultar uma tabela que não existe (OperationalError)
1
2
conexao = sqlite3.connect("loja.db")
conexao.execute("SELECT * FROM produto")
1
sqlite3.OperationalError: no such table: produto
Causas típicas: nome errado (produto em vez de produtos), tabela ainda não criada, ou o script rodando em outra pasta, o que faz o connect() criar um loja.db novo e vazio. Correção: confira o nome, use CREATE TABLE IF NOT EXISTS e, se preciso, um caminho absoluto para o arquivo.
Inserir um valor repetido em coluna UNIQUE (IntegrityError)
Rodar de novo o script que insere ("Teclado", 199.9, 15) na tabela produtos (coluna nome é UNIQUE):
1
sqlite3.IntegrityError: UNIQUE constraint failed: produtos.nome
Correção: trate sqlite3.IntegrityError com try/except, verifique antes com um SELECT, ou use INSERT OR IGNORE quando repetir não for problema.
Número de valores diferente do número de ? (ProgrammingError)
1
2
conexao.execute("CREATE TABLE t (a TEXT, b TEXT)")
conexao.execute("INSERT INTO t VALUES (?, ?)", ("x",))
1
sqlite3.ProgrammingError: Incorrect number of bindings supplied. The current statement uses 2, and there are 1 supplied.
Correção: a tupla precisa ter exatamente um valor para cada ?.
Usar a conexão depois de fechar (ProgrammingError)
1
2
3
conexao = sqlite3.connect("loja.db")
conexao.close()
conexao.execute("SELECT * FROM produtos")
1
sqlite3.ProgrammingError: Cannot operate on a closed database.
Correção: faça todas as operações antes do close(). Com contextlib.closing(), mantenha o código que usa a conexão dentro do bloco with.
Exercícios resolvidos
Tente resolver cada exercício antes de abrir a solução. Todos os códigos foram executados no Python 3.13 e as saídas conferidas. Para mais prática, veja a nossa página de exercícios de Python resolvidos.
Exercício 1. Crie um banco em memória com a tabela livros (titulo TEXT, autor TEXT, ano INTEGER), insira os 4 livros abaixo com uma única chamada e mostre quantos registros existem.
Ver solução
1
2
3
4
5
6
7
8
9
10
11
12
13
import sqlite3
conexao = sqlite3.connect(":memory:")
conexao.execute("CREATE TABLE livros (titulo TEXT, autor TEXT, ano INTEGER)")
livros = [
("Dom Casmurro", "Machado de Assis", 1899),
("Aprendendo Python", "Ana Souza", 2015),
("O Alquimista", "Paulo Coelho", 1988),
("Python Avançado 2a ed.", "Ana Souza", 2022),
]
conexao.executemany("INSERT INTO livros VALUES (?, ?, ?)", livros)
conexao.commit()
print(conexao.execute("SELECT COUNT(*) FROM livros").fetchone()[0])
Saída: 4
O executemany() roda o INSERT uma vez por tupla. O fetchone() devolve a tupla (4,), e o [0] pega o número.
Exercício 2. Usando a tabela do Exercício 1, mostre os livros publicados depois de 2000, do mais antigo para o mais novo, no formato ano - título. O ano mínimo deve vir de uma variável.
Ver solução
1
2
3
4
5
6
ano_minimo = 2000
cursor = conexao.execute(
"SELECT titulo, ano FROM livros WHERE ano > ? ORDER BY ano", (ano_minimo,)
)
for titulo, ano in cursor:
print(f"{ano} - {titulo}")
Saída:
1
2
2015 - Aprendendo Python
2022 - Python Avançado 2a ed.
A variável entra pelo placeholder ?, e o for desempacota cada tupla em titulo e ano.
Exercício 3. Ainda na tabela livros, devolva os livros da autora “Ana Souza” como uma lista de dicionários com as chaves titulo e autor.
Ver solução
1
2
3
4
5
6
conexao.row_factory = sqlite3.Row
linhas = conexao.execute(
"SELECT titulo, autor FROM livros WHERE autor = ?", ("Ana Souza",)
).fetchall()
resultado = [dict(linha) for linha in linhas]
print(resultado)
Saída:
1
[{'titulo': 'Aprendendo Python', 'autor': 'Ana Souza'}, {'titulo': 'Python Avançado 2a ed.', 'autor': 'Ana Souza'}]
Com sqlite3.Row, cada linha vira um dicionário com dict(linha), e as chaves são os nomes das colunas do SELECT.
Exercício 4. Escreva buscar_por_trecho(conexao, trecho) que devolva os títulos que contêm o trecho informado (busca parcial, sem diferenciar maiúsculas), usando placeholder. Teste com "python" e "java".
Ver solução
1
2
3
4
5
6
def buscar_por_trecho(conexao, trecho):
sql = "SELECT titulo FROM livros WHERE titulo LIKE ? ORDER BY titulo"
return [linha[0] for linha in conexao.execute(sql, (f"%{trecho}%",))]
print(buscar_por_trecho(conexao, "python"))
print(buscar_por_trecho(conexao, "java"))
Saída:
1
2
['Aprendendo Python', 'Python Avançado 2a ed.']
[]
Os % do LIKE vão no valor, não no SQL: a f-string aqui monta só o dado ("%python%"), e o placeholder continua protegendo a consulta. No SQLite, o LIKE não diferencia maiúsculas para letras sem acento.
Exercício 5. Escreva atualizar_ano(conexao, titulo, novo_ano) que atualize o ano de um livro dentro de um with conexao: e devolva quantas linhas foram alteradas. Teste com um título que existe e com um que não existe.
Ver solução
1
2
3
4
5
6
7
8
9
def atualizar_ano(conexao, titulo, novo_ano):
with conexao:
cursor = conexao.execute(
"UPDATE livros SET ano = ? WHERE titulo = ?", (novo_ano, titulo)
)
return cursor.rowcount
print(atualizar_ano(conexao, "O Alquimista", 1987))
print(atualizar_ano(conexao, "Livro que não existe", 2000))
Saída:
1
2
1
0
O with faz o commit ao sair do bloco. Um rowcount de 0 indica que o WHERE não encontrou nada, sem gerar exceção.
Exercício 6. Crie a tabela usuarios (email TEXT UNIQUE) e tente inserir [email protected], [email protected] e [email protected] de novo, em um único lote dentro de with conexao:. Mostre a mensagem de erro e quantos usuários ficaram gravados.
Ver solução
1
2
3
4
5
6
7
8
9
10
11
conexao.execute("CREATE TABLE usuarios (email TEXT UNIQUE)")
conexao.commit()
novos = [("[email protected]",), ("[email protected]",), ("[email protected]",)]
try:
with conexao:
conexao.executemany("INSERT INTO usuarios VALUES (?)", novos)
except sqlite3.IntegrityError as erro:
print("Lote cancelado:", erro)
print(conexao.execute("SELECT COUNT(*) FROM usuarios").fetchone()[0])
Saída:
1
2
Lote cancelado: UNIQUE constraint failed: usuarios.email
0
O terceiro e-mail violou o UNIQUE, o with fez rollback e nenhum dos três ficou gravado, nem os dois primeiros. É o “tudo ou nada” da transação.
Exercício 7 (estilo prova). A variável nome vem de um formulário web. Qual das linhas abaixo consulta o banco de forma segura contra SQL injection?
a) cursor.execute(f"SELECT * FROM clientes WHERE nome = '{nome}'")
b) cursor.execute("SELECT * FROM clientes WHERE nome = '%s'" % nome)
c) cursor.execute("SELECT * FROM clientes WHERE nome = '" + nome + "'")
d) cursor.execute("SELECT * FROM clientes WHERE nome = ?", (nome,))
Ver solução
Resposta: d. As alternativas a, b e c colam o texto do usuário dentro do SQL (f-string, formatação com % e concatenação são a mesma coisa nesse sentido), então um valor como x' OR '1'='1 altera a consulta. Na d, o valor vai separado pelo placeholder ? e é sempre tratado como dado.
Exercício 8 (estilo prova). O arquivo notas.db não existe antes da execução. O que o código abaixo imprime?
1
2
3
4
5
6
7
8
9
10
import sqlite3
conexao = sqlite3.connect("notas.db")
conexao.execute("CREATE TABLE IF NOT EXISTS notas (valor REAL)")
conexao.execute("INSERT INTO notas VALUES (?)", (8.5,))
conexao.close()
conexao = sqlite3.connect("notas.db")
print(conexao.execute("SELECT COUNT(*) FROM notas").fetchone()[0])
conexao.close()
a) 1
b) 0
c) sqlite3.OperationalError: no such table: notas
d) 8.5
Ver solução
Saída: 0
Resposta: b. O CREATE TABLE foi gravado (no modo padrão, o sqlite3 só abre transação automática para INSERT, UPDATE, DELETE e REPLACE), então a tabela existe e não ocorre o erro da alternativa c. Já o INSERT ficou pendente e o close() não faz commit, então a nota foi descartada e a contagem é zero.
Quer praticar bancos de dados em projetos completos, do script ao sistema web? Conheça o nosso curso de Python completo.
Conclusão
Neste guia de SQLite com Python, você aprendeu:
✅ Conectar e criar tabelas - sqlite3.connect(), cursor e CREATE TABLE IF NOT EXISTS
✅ Placeholders - ? e :nome no lugar de f-string, e por que isso evita SQL injection
✅ executemany - inserir listas de registros de uma vez
✅ Consultas - fetchone(), fetchall(), fetchmany() e sqlite3.Row
✅ Transações - commit(), rollback() e with conexao:
✅ Banco em memória - :memory: para testes e dados temporários
Próximos passos:
- Veja como o Django cria e evolui tabelas com o comando migrate
- Revise dicionários no Python para trabalhar com o resultado das consultas
- Leia a documentação oficial do sqlite3
Se ficou com alguma dúvida, fique à vontade para deixar um comentário no box aqui embaixo! Será um prazer te responder! ![]()
Perguntas frequentes
Como usar SQLite no Python?
O Python já vem com o módulo sqlite3 na biblioteca padrão, então não é preciso instalar nada. Importe com import sqlite3, abra o banco com conexao = sqlite3.connect('banco.db') (o arquivo é criado se não existir), execute comandos SQL com conexao.execute(), confirme as alterações com conexao.commit() e feche com conexao.close().
Por que usar ? em vez de f-string nas consultas do sqlite3?
Porque montar SQL com f-string permite SQL injection: se o valor vier do usuário, um texto como admin' -- altera a própria consulta e pode, por exemplo, liberar um login sem senha. Com placeholders (execute('SELECT * FROM usuarios WHERE nome = ?', (nome,))), o sqlite3 envia o valor separado do comando e ele é sempre tratado como dado, nunca como SQL.
Qual a diferença entre fetchone, fetchall e fetchmany?
fetchone() devolve a próxima linha do resultado como tupla, ou None se não houver mais linhas. fetchall() devolve todas as linhas restantes em uma lista. fetchmany(n) devolve uma lista com até n linhas, útil para processar resultados grandes em lotes. Também é possível percorrer o cursor diretamente com for linha in cursor:.
Preciso chamar commit() no sqlite3?
Sim, depois de INSERT, UPDATE e DELETE. No modo padrão, o sqlite3 abre uma transação automaticamente antes desses comandos, e as alterações só ficam gravadas no arquivo após conexao.commit(). Fechar a conexão com close() não faz commit: o que não foi confirmado é descartado. Usar with conexao: faz o commit automaticamente se o bloco terminar sem erro.
O with sqlite3.connect() fecha a conexão?
Não. O with conexao: controla a transação: faz commit() se o bloco terminar sem erro e rollback() se ocorrer uma exceção, mas a conexão continua aberta depois do bloco. Para fechar automaticamente, use contextlib.closing(sqlite3.connect('banco.db')) ou chame conexao.close().
Como retornar as linhas do sqlite3 como dicionário?
Defina conexao.row_factory = sqlite3.Row antes de executar a consulta. Cada linha passa a aceitar acesso por nome de coluna (linha['nome']) e por índice, e pode ser convertida em dicionário com dict(linha). Para uma lista de dicionários: [dict(l) for l in conexao.execute('SELECT ...')].
Qual o erro ao passar uma string em vez de tupla para o execute do sqlite3?
Escrever execute('... VALUES (?)', ('Maria')) gera sqlite3.ProgrammingError: Incorrect number of bindings supplied. The current statement uses 1, and there are 5 supplied. Os parênteses sozinhos não criam uma tupla, então o sqlite3 trata cada letra de ‘Maria’ como um parâmetro. A correção é a vírgula: ('Maria',).
Em uma questão de prova, qual comando do sqlite3 insere várias linhas de uma vez?
O método executemany(). Ele recebe o comando SQL com placeholders e uma lista de tuplas, executando o comando uma vez para cada tupla: cursor.executemany('INSERT INTO produtos VALUES (?, ?)', lista). O execute() executa um único comando, e o executescript() executa um script SQL inteiro, sem suporte a placeholders.
"Porque o Senhor dá a sabedoria, e da sua boca vem a inteligência e o entendimento" Pv 2:6