Python trae SQLite de serie: el módulo sqlite3 no necesita instalar nada. Este ejemplo reúne lo que hace falta para usar SQLite desde Python en un programa real, sobre la base «tienda» guardada en un archivo tienda.db.
También te puede interesar
Los programas se ejecutaron con Python 3.13 en un contenedor limpio:
versionesPython 3.13.16 · SQLite 3.46.1
Consultar con parámetros
Los valores van aparte, como ? (por posición) o :nombre (con un diccionario). Con row_factory = sqlite3.Row, las filas se leen por nombre de columna:
import sqlite3
con = sqlite3.connect('/tmp/tienda.db')
con.row_factory = sqlite3.Row # filas que se leen por nombre de columna
pais = 'México'
filas = con.execute(
"""SELECT c.nombre, COUNT(p.id) AS pedidos
FROM clientes c LEFT JOIN pedidos p ON p.cliente_id = c.id
WHERE c.pais = ?
GROUP BY c.id, c.nombre
ORDER BY pedidos DESC, c.nombre""",
(pais,),
).fetchall()
for f in filas:
print(f"{f['nombre']:<16} {f['pedidos']} pedidos")
# Un solo valor: fetchone() devuelve una fila o None
total = con.execute(
"SELECT SUM(cantidad * precio_unitario) FROM detalle WHERE pedido_id = :pedido",
{'pedido': 8},
).fetchone()[0]
print(f"Pedido 8: ${total:,.2f} USD")
con.close()salida · python consultar.pyAna Torres 4 pedidos Andrés Navarro 2 pedidos Diego Martínez 2 pedidos Jorge Ramírez 2 pedidos Pedido 8: $3,638.78 USD
Insertar en lote y confirmar
executemany ejecuta la misma sentencia con cada tupla. El bloque with con: confirma (COMMIT) al salir, sin tener que acordarse:
import sqlite3
con = sqlite3.connect('/tmp/tienda.db')
nuevos = [
(16, 'Hub USB-C', 3, 34.90, 12),
(17, 'Alfombrilla XL', 3, 19.50, 40),
(18, 'Funda para portátil', None, 24.00, 30),
]
with con: # al salir del bloque hace COMMIT; si algo falla, ROLLBACK
cur = con.executemany(
"INSERT INTO productos (id, nombre, categoria_id, precio, stock) VALUES (?, ?, ?, ?, ?)",
nuevos,
)
print('filas insertadas:', cur.rowcount)
print('productos ahora:', con.execute('SELECT COUNT(*) FROM productos').fetchone()[0])
con.close()salida · python insertar.pyfilas insertadas: 3 productos ahora: 18
Todo o nada: la transacción
Si algo falla dentro del with, Python deshace todo el bloque (ROLLBACK). Aquí el segundo producto no cumple CHECK (precio > 0), y el primero tampoco se guarda:
import sqlite3
con = sqlite3.connect('/tmp/tienda.db')
antes = con.execute('SELECT COUNT(*) FROM productos').fetchone()[0]
# Dos productos: el segundo tiene precio 0 y no pasa el CHECK (precio > 0)
lote = [(19, 'Soporte de monitor', 2, 45.00, 10), (20, 'Producto de prueba', 3, 0, 1)]
try:
with con:
con.executemany(
"INSERT INTO productos (id, nombre, categoria_id, precio, stock) VALUES (?, ?, ?, ?, ?)", lote)
except sqlite3.IntegrityError as e:
print('Error:', e)
despues = con.execute('SELECT COUNT(*) FROM productos').fetchone()[0]
print('productos antes:', antes, '| después:', despues, '(el primero tampoco se guardó)')
con.close()salida · python transaccion.pyError: CHECK constraint failed: precio > 0 productos antes: 18 | después: 18 (el primero tampoco se guardó)
Por qué nunca hay que pegar texto en la consulta
Si el texto que escribe el usuario se mete dentro de la consulta con una f-string, el usuario puede escribir SQL. Con una comilla y un OR bien puestos, la búsqueda devuelve todos los clientes:
import sqlite3
con = sqlite3.connect('/tmp/tienda.db')
lo_que_escribe_el_usuario = "x' OR '1'='1"
# MAL: el texto del usuario se pega dentro de la consulta
sql = f"SELECT nombre FROM clientes WHERE nombre = '{lo_que_escribe_el_usuario}'"
print('consulta armada:', sql)
print('pegando texto: ', len(con.execute(sql).fetchall()), 'clientes')
# BIEN: el valor va aparte, como parámetro; nunca se interpreta como SQL
filas = con.execute("SELECT nombre FROM clientes WHERE nombre = ?", (lo_que_escribe_el_usuario,)).fetchall()
print('con parámetro: ', len(filas), 'clientes')
con.close()salida · python inyeccion.pyconsulta armada: SELECT nombre FROM clientes WHERE nombre = 'x' OR '1'='1' pegando texto: 12 clientes con parámetro: 0 clientes
Con el parámetro, el texto se trata como un valor, nunca como SQL, y no coincide con ningún nombre. Es la inyección SQL, que se explica a fondo en la lección 8 del curso.
Fechas: el aviso de Python 3.12+
Pasar un datetime.date como parámetro todavía funciona, pero usa un adaptador por defecto que Python declaró obsoleto en la 3.12. La alternativa es convertirlo tú en texto ISO 8601, que además es lo que SQLite sabe comparar:
import datetime, sqlite3
con = sqlite3.connect('/tmp/tienda.db')
# Pasar un date directamente usa el adaptador por defecto, obsoleto desde Python 3.12
con.execute("INSERT INTO pedidos (id, cliente_id, fecha, estado) VALUES (?, ?, ?, ?)",
(23, 3, datetime.date(2026, 10, 20), 'pendiente'))
# Mejor: convertirlo tú a texto ISO 8601, el formato que SQLite sabe comparar
con.execute("INSERT INTO pedidos (id, cliente_id, fecha, estado) VALUES (?, ?, ?, ?)",
(24, 3, datetime.date(2026, 10, 20).isoformat(), 'pendiente'))
for fila in con.execute("SELECT id, fecha, typeof(fecha) FROM pedidos WHERE id IN (23, 24)"):
print(fila)
fecha = con.execute("SELECT fecha FROM pedidos WHERE id = 24").fetchone()[0]
print('lo que vuelve:', repr(fecha), '→', repr(datetime.date.fromisoformat(fecha)))
con.close()salida · python fechas.py/w/fechas.py:5: DeprecationWarning: The default date adapter is deprecated as of Python 3.12; see the sqlite3 documentation for suggested replacement recipes
con.execute("INSERT INTO pedidos (id, cliente_id, fecha, estado) VALUES (?, ?, ?, ?)",
(23, '2026-10-20', 'text')
(24, '2026-10-20', 'text')
lo que vuelve: '2026-10-20' → datetime.date(2026, 10, 20)Las dos filas quedan iguales, como texto. Al leer, la fecha vuelve como str; date.fromisoformat la convierte en objeto.
| Para… | Usa |
|---|---|
| Un valor en la consulta | ? o :nombre, nunca f-strings ni + |
| Leer por nombre de columna | con.row_factory = sqlite3.Row |
| Muchas filas | executemany |
| Confirmar o deshacer | with con: |
| Fechas | .isoformat() al guardar, date.fromisoformat() al leer |
Todo el código de esta página se ejecutó con Python 3.13 y su SQLite 3.46.1 en un contenedor limpio antes de publicarla; las salidas están copiadas de esa ejecución. Documentación oficial: módulo sqlite3 de Python.