DELETE: borrar filas en SQLite, sin tocar nada más
DELETE saca filas de una tabla. No elimina la tabla, no modifica su esquema y no afecta a otras tablas (salvo que hayas configurado borrados en cascada). La sintaxis es breve:
DELETE FROM users WHERE id = 2; busca las filas que cumplen la condición y las elimina. Las otras dos filas quedan intactas. La tabla sigue existiendo: puedes seguir insertando datos en ella.
La idea clave es esta: DELETE funciona como un SELECT que, en vez de devolverte las filas que coinciden, las tira a la basura.
La cláusula WHERE es la que manda
Cualquier DELETE serio depende por completo de su cláusula WHERE. Si la escribes bien, borras justo lo que querías. Si te equivocas, te llevas por delante muchas más filas de la cuenta —a veces la tabla entera—.
Los dos borradores sin publicar y sin visitas desaparecieron. Las filas publicadas sobreviven porque la condición no las afecta. Puedes usar cualquier expresión que admita WHERE: IN, LIKE, BETWEEN, subconsultas, combinaciones con AND/OR.
Un hábito que vale la pena adoptar: antes de ejecutar un DELETE, prueba la misma cláusula WHERE con un SELECT.
-- Vista previa de lo que se eliminará:
SELECT * FROM posts WHERE published = 0 AND views = 0;
-- ¿Conforme con las filas? Ahora elimínalas:
DELETE FROM posts WHERE published = 0 AND views = 0;
Ese baile en dos pasos ha salvado más bases de datos que todas las herramientas de backup juntas.
DELETE sin WHERE: cómo borrar todas las filas en SQLite
Si omites WHERE, el comando DELETE elimina absolutamente todas las filas de la tabla:
La tabla queda vacía, pero sigue existiendo. SQLite no tiene una sentencia TRUNCATE: el equivalente es DELETE FROM tabla;, y por dentro SQLite aplica una "optimización de truncado" que descarta todas las páginas de golpe en lugar de ir borrando fila por fila. Es rápido, aunque sigue siendo una operación transaccional que puedes revertir.
Si usaste AUTOINCREMENT en la clave primaria, el contador no se reinicia solo. Para que los ids vuelvan a empezar en 1, hay que limpiar también la fila de la secuencia:
DELETE FROM log;
DELETE FROM sqlite_sequence WHERE name = 'log';
Para una INTEGER PRIMARY KEY normal (sin AUTOINCREMENT), SQLite ya reutiliza los ids sin problema, así que esto no hace falta.
Borrar varias filas concretas
IN es la forma más limpia de borrar un conjunto conocido de filas:
También puedes apoyarte en una subconsulta para definir qué filas borrar; resulta muy práctico cuando los registros que quieres eliminar dependen de un join o de otra tabla:
SQLite no admite la sintaxis DELETE ... JOIN como sí lo hace MySQL, pero una subconsulta dentro del WHERE hace exactamente lo mismo.
RETURNING: ver qué filas se borraron
Si añades RETURNING a tu sentencia, SQLite te devuelve las filas eliminadas como un conjunto de resultados, igual que si fuera un SELECT:
Recibes el id y el email de cada fila eliminada. Esto resulta invaluable para:
- Registrar exactamente qué se borró.
- Implementar funciones de deshacer (guarda en algún lado las filas devueltas).
- Confirmar, en una sola ida y vuelta, que el delete afectó justo las filas que esperabas.
RETURNING funciona con INSERT, UPDATE y DELETE. Lo cubrimos en detalle en su propia página.
ON DELETE CASCADE para filas relacionadas
Cuando una tabla padre y una tabla hija están vinculadas mediante una clave foránea, borrar al padre deja huérfanos a los hijos — salvo que le indiques a SQLite que aplique cascada:
Al borrar el autor también se eliminan sus libros. Sin ON DELETE CASCADE, ese mismo DELETE o bien terminaría dejando libros huérfanos (si las claves foráneas están desactivadas), o bien fallaría con un error de restricción (si están activas).
El gran detalle peligroso: en SQLite las claves foráneas vienen desactivadas por defecto. Tienes que ejecutar PRAGMA foreign_keys = ON; en cada conexión. Si no activas el pragma, ON DELETE CASCADE se ignora silenciosamente y los libros se quedan ahí. La mayoría de los drivers de aplicación lo configuran por ti o te dan una opción para hacerlo; revisa el tuyo.
Otras opciones de cascada que conviene conocer: ON DELETE SET NULL (pone la clave foránea en NULL), ON DELETE RESTRICT (rechaza el borrado si hay registros hijos) y ON DELETE NO ACTION (el comportamiento por defecto, prácticamente igual que RESTRICT en la mayoría de los casos).
DELETE con LIMIT (opción de compilación)
Algunas compilaciones de SQLite admiten DELETE ... LIMIT, lo cual resulta útil para ir borrando filas en lotes cuando trabajas con tablas enormes:
DELETE FROM logs
WHERE created_at < '2024-01-01'
ORDER BY created_at
LIMIT 1000;
Esto requiere que SQLite se haya compilado con SQLITE_ENABLE_UPDATE_DELETE_LIMIT. Los binarios oficiales y la mayoría de los bindings (el sqlite3 de Python o better-sqlite3 de Node) ya lo traen activado. Si el tuyo no lo tiene, vas a recibir un error de sintaxis; en ese caso, tira de una subconsulta:
DELETE FROM logs
WHERE id IN (
SELECT id FROM logs
WHERE created_at < '2024-01-01'
ORDER BY created_at
LIMIT 1000
);
Hacer borrados por lotes mantiene las transacciones pequeñas, algo clave cuando otras conexiones están leyendo la base de datos al mismo tiempo.
Envuelve los borrados grandes en una transacción
Un DELETE ya es transaccional de forma implícita: o se borran todas las filas que coinciden, o no se borra ninguna. Aun así, cuando vas a eliminar muchos registros, encerrar la operación en una transacción explícita te permite hacer ROLLBACK si algo no cuadra:
ROLLBACK deshace el borrado por completo. En una sesión real, harías COMMIT una vez que el conteo te cuadre. Las transacciones también son muchísimo más rápidas cuando borras muchas filas con sentencias sueltas: envolver el bucle entre BEGIN y COMMIT evita un fsync por cada delete.
Cosas que NO borra un DELETE
Vale la pena aclarar algunas confusiones habituales:
DELETE FROM tabla;vacía la tabla, pero no la elimina. Para quitar la tabla en sí usaDROP TABLE tabla;.DELETEno reduce el tamaño del archivo de la base de datos. Las páginas quedan marcadas como libres para reutilizarse. Si quieres recuperar espacio en disco, ejecutaVACUUM;(lo vemos en el capítulo de rendimiento).- Borrar una fila no borra las filas hijas en otras tablas, salvo que tengas configurado
ON DELETE CASCADEy las claves foráneas estén activadas. - Un
DELETEque no afecta a ninguna fila no es un error. Es una sentencia ejecutada con éxito ychanges() = 0. Si necesitas saberlo, revisa el conteo de filas.
Lo que viene: UPSERT
Muchas veces no quieres borrar de verdad: lo que buscas es insertar si la fila es nueva o actualizar si ya existe. SQLite lo llama UPSERT, y la cláusula ON CONFLICT lo resuelve en una sola sentencia en vez de tres. Eso es lo que veremos a continuación.
Preguntas frecuentes
¿Cómo se borra una fila en SQLite?
Con DELETE FROM nombre_tabla WHERE condicion;. La cláusula WHERE decide qué filas se van. Por ejemplo, DELETE FROM users WHERE id = 7; elimina solo al usuario con id 7. Ojo: si te olvidas del WHERE, te cargas todas las filas de la tabla.
¿Cómo borro todas las filas de una tabla en SQLite?
Ejecuta DELETE FROM nombre_tabla; sin WHERE. SQLite no tiene TRUNCATE, así que un DELETE sin filtro hace ese mismo papel y, además, internamente SQLite lo optimiza (la llamada truncate optimization). Si también quieres reiniciar los contadores de AUTOINCREMENT, borra después las filas correspondientes en sqlite_sequence.
¿SQLite puede propagar el DELETE a tablas relacionadas?
Sí, siempre que declares ON DELETE CASCADE en la clave foránea y tengas activadas las foreign keys con PRAGMA foreign_keys = ON;. En SQLite las claves foráneas vienen desactivadas por defecto, así que ese pragma es clave: sin él, los CASCADE se ignoran en silencio.
¿Cómo veo qué filas se han borrado?
Añade una cláusula RETURNING: DELETE FROM users WHERE active = 0 RETURNING id, email; te devuelve las filas eliminadas como si fuera un SELECT. Va muy bien para registrar logs, montar un undo o simplemente confirmar que has borrado justo lo que querías.