DELETE, TRUNCATE y DROP TABLE borran datos, pero no son intercambiables. DELETE es DML (Data Manipulation Language, lenguaje de manipulación de datos): borra filas que cumplen una condición, de a una, y puede revertirse con ROLLBACK. TRUNCATE y DROP TABLE son DDL (Data Definition Language, lenguaje de definición de datos): TRUNCATE vacía la tabla entera sin condición, y DROP TABLE la elimina junto con su estructura. La sorpresa está en el motor: en PostgreSQL las dos son transaccionales y se revierten sin drama; en MySQL con InnoDB provocan un COMMIT implícito y no hay vuelta atrás; SQLite ni siquiera tiene TRUNCATE. Elegir mal cuál usar puede costarte datos que creías poder recuperar.
Ver índice de contenidos
Qué borra cada uno y de qué tipo es
Las tres sentencias borran algo, pero apuntan a blancos distintos. DELETE borra filas: podés decirle exactamente cuáles con una cláusula WHERE, o dejarlo sin condición y borrar todas, una por una. TRUNCATE TABLE vacía la tabla entera de una sola vez: no acepta condición, y la tabla sigue existiendo, vacía. DROP TABLE va más allá: elimina la tabla completa, con sus filas, estructura, índices y triggers. Después de un DROP TABLE no hay «tabla vacía» a la que volver. Si todavía no tenés claro cómo se arma una consulta SQL básica, conviene repasar antes introducción a SQL desde cero.
DML y DDL
Esta diferencia corresponde a una clasificación del propio lenguaje SQL. DELETE pertenece al DML (Data Manipulation Language): trabaja sobre las filas. TRUNCATE y DROP TABLE pertenecen al DDL (Data Definition Language): trabajan sobre la estructura, aunque TRUNCATE de paso vacíe el contenido. La documentación de MySQL lo aclara para TRUNCATE TABLE, y la distinción es la misma en PostgreSQL y en SQLite, con la salvedad de que SQLite no tiene TRUNCATE.
| Sentencia | Qué borra | Clasificación |
|---|---|---|
| DELETE | Filas que cumplen una condición (o todas, sin WHERE) | DML |
| TRUNCATE TABLE | Todas las filas; la tabla sigue existiendo, vacía | DDL |
| DROP TABLE | La tabla completa: filas, estructura, índices y triggers | DDL |
El punto que más sorprende: la transaccionalidad
Acá es donde DELETE, TRUNCATE y DROP dejan de comportarse igual entre motores. Si ya sabés qué es una transacción y qué hacen COMMIT y ROLLBACK, saltá directo a la tabla; si no, conviene repasar antes transacciones y ACID en bases de datos.
| Aspecto | PostgreSQL | MySQL (InnoDB) | SQLite |
|---|---|---|---|
| ¿Existe TRUNCATE? | Sí | Sí | No: se usa DELETE FROM tabla sin WHERE |
| TRUNCATE / DROP TABLE, ¿son transaccionales? | Sí: se revierten con ROLLBACK | No: provocan un COMMIT implícito, no se pueden revertir | No aplica TRUNCATE; el DELETE sí es transaccional |
| ¿Qué pasa con una transacción ya abierta? | Sigue abierta, sin efectos extra | Se cierra sola por el commit implícito | Sigue abierta, sin efectos extra |
| Contador o secuencia al vaciar | No cambia, salvo RESTART IDENTITY | El AUTO_INCREMENT se resetea | No aplica (no hay TRUNCATE) |
-- PostgreSQL: TRUNCATE es transaccional
BEGIN;
TRUNCATE TABLE pedidos;
ROLLBACK;
-- pedidos conserva sus filas: el TRUNCATE se revirtió sin dejar rastro
-- MySQL con InnoDB: TRUNCATE hace un commit implícito
START TRANSACTION;
DELETE FROM clientes WHERE id = 8;
TRUNCATE TABLE pedidos; -- acá ya se confirmó todo, sin que pidieras COMMIT
ROLLBACK; -- no revierte nada: no queda ninguna transacción abierta
-- SQLite no tiene TRUNCATE: se usa DELETE FROM sin WHERE
BEGIN;
DELETE FROM pedidos;
ROLLBACK;
-- esto sí revierte el borrado completo: sigue siendo DML transaccional
El caso de MySQL es el que más sorprende: además de no poder deshacer el TRUNCATE o el DROP TABLE, cualquier transacción abierta con otros cambios sin confirmar se cierra en ese mismo momento. Si estabas en medio de una operación con varios pasos y metés un TRUNCATE pensando en arrepentirte después, en MySQL con InnoDB ya es tarde: se guardó todo, lo que tenías antes incluido.
Como no hay ROLLBACK posible, la única red de seguridad real es una copia de seguridad previa. Si no tenés una rutina de respaldo probada, revisá copias de seguridad y restauración de bases de datos antes de ejecutar cualquiera de las dos en producción.
Qué pasa con los contadores
Vaciar una tabla no siempre reinicia la numeración de sus filas. En MySQL, TRUNCATE TABLE resetea el contador AUTO_INCREMENT: la próxima fila insertada vuelve a empezar desde el valor inicial, normalmente 1. En PostgreSQL el comportamiento por defecto es el opuesto: TRUNCATE no toca las secuencias asociadas —CONTINUE IDENTITY, la opción implícita—, así que la próxima fila sigue la numeración donde había quedado. Para el mismo efecto que en MySQL hay que pedirlo con RESTART IDENTITY.
TRUNCATE TABLE pedidos; -- PostgreSQL: CONTINUE IDENTITY implícito, la secuencia no se toca
TRUNCATE TABLE pedidos RESTART IDENTITY; -- reinicia la secuencia asociada
DELETE, en cambio, no resetea contadores en ningún motor: borrar todas las filas con un DELETE FROM tabla sin WHERE deja la secuencia o el AUTO_INCREMENT donde estaba. Es fácil de comprobar: si insertás después de un DELETE masivo, el identificador sigue la numeración anterior; si insertás después de un TRUNCATE en MySQL, arranca de nuevo.
Claves foráneas: cuándo fallan
Una clave foránea (FK, de Foreign Key) es una restricción que obliga a que un valor de una tabla exista también en otra. TRUNCATE, al vaciar la tabla de golpe, choca con esa restricción cuando otra tabla la referencia. En MySQL con InnoDB, TRUNCATE TABLE falla directamente si existen claves foráneas de otras tablas apuntando a la que querés vaciar. En PostgreSQL el comportamiento por defecto es el mismo —también falla—, salvo que incluyas en el mismo TRUNCATE las tablas dependientes, o agregues CASCADE.
Acá conviene un matiz que se presta a confusión, porque CASCADE significa cosas distintas según el contexto. En un TRUNCATE con CASCADE en PostgreSQL, lo que se vacía en cascada son las tablas dependientes: si truncás pedidos y detalles_pedido tiene una FK hacia pedidos, CASCADE también vacía detalles_pedido. Es distinto de un DROP TABLE con CASCADE, donde CASCADE no borra la tabla referenciada: elimina la restricción de clave foránea que dependía de la tabla borrada, y la otra tabla queda intacta. Confundir los dos usos es un error potencialmente grave: uno vacía datos de otra tabla, el otro solo quita una regla de integridad.
-- PostgreSQL: los dos CASCADE no hacen lo mismo
TRUNCATE TABLE pedidos CASCADE; -- también vacía las tablas que dependen de pedidos
DROP TABLE pedidos CASCADE; -- borra pedidos y elimina las FK que dependían de ella (no borra esas tablas)
Con DROP TABLE el comportamiento es parecido en ambos motores: si otra tabla tiene una clave foránea apuntando a la que querés eliminar, el DROP falla salvo que uses CASCADE (con el significado que acabás de ver) o borres antes la restricción o la tabla dependiente.
Triggers: qué se dispara y qué no
Un trigger es un bloque de código que la base de datos ejecuta automáticamente ante un evento sobre una tabla, como un INSERT, un UPDATE o un DELETE. DELETE, al procesar fila por fila, dispara los triggers ON DELETE definidos sobre la tabla: si tenés uno que registra bajas o descuenta stock, se ejecuta por cada fila borrada. TRUNCATE no pasa por ese camino: en MySQL, TRUNCATE TABLE no dispara los triggers ON DELETE, así que cualquier lógica que dependa de ellos simplemente no se ejecuta. Es una fuente de errores difíciles de detectar, porque a simple vista TRUNCATE «parece» un DELETE de todas las filas, pero no activa lo mismo.
DROP TABLE elimina la tabla entera, y con ella sus triggers: no queda nada que disparar, porque no queda tabla. En SQLite, además, el proceso interno de DROP TABLE borra primero los triggers y recién después aplica un DELETE implícito para mantener las claves foráneas, así que tampoco se disparan.
Por qué DELETE es más lento
A igualdad de filas borradas, DELETE tarda más que TRUNCATE o DROP TABLE. DELETE procesa y registra el borrado fila por fila: por cada una verifica la condición del WHERE, dispara los triggers correspondientes y guarda lo necesario para poder revertir con ROLLBACK. Ese registro fila por fila es lo que le permite ser reversible y disparar triggers, pero tiene un costo.
TRUNCATE y DROP TABLE no necesitan nada de eso: operan a nivel de página de datos o sobre los metadatos de la tabla, sin recorrer ni registrar cada fila. La documentación de MySQL lo dice de forma explícita: cuando no importa cuántas filas se borraron ni hace falta disparar triggers ON DELETE, TRUNCATE TABLE es más rápido que un DELETE sin WHERE.
Esto no convierte a TRUNCATE en «un DELETE rápido» sin más: es una operación distinta, con las otras consecuencias que ya viste. La velocidad es real, pero no alcanza como único criterio para elegir.
Cuál usar en cada caso
- ¿Necesitás borrar algunas filas según una condición?Usá DELETE. Es la única de las tres que acepta WHERE, y la que dispara triggers ON DELETE.
- ¿Necesitás vaciar una tabla completa y te importa la velocidad?Usá TRUNCATE, sabiendo que en MySQL no hay vuelta atrás y que puede resetear el contador.
- ¿La tabla ya no tiene ningún uso futuro?Usá DROP TABLE, con una copia de seguridad hecha antes si hay la mínima duda.
- ¿Estás en MySQL, dentro de una transacción con varios pasos, sin confirmar todavía?Evitá TRUNCATE y DROP TABLE hasta el final: cierran la transacción antes de tiempo.
Las precauciones generales antes de borrar —probar primero con un SELECT, trabajar sobre una copia— ya están cubiertas en cómo eliminar registros duplicados con SQL. Esta guía no repite esas prácticas: se enfoca en la diferencia entre las tres sentencias.
Errores frecuentes
- Creer que TRUNCATE es «un DELETE rápido» sin más. Cambia la transaccionalidad, los contadores, los triggers y el comportamiento con claves foráneas.
- Confiar en el ROLLBACK después de un TRUNCATE sin conocer el motor. Funciona en PostgreSQL; en MySQL con InnoDB ya es tarde, el commit fue implícito.
- Confundir «borrar todas las filas» con «borrar la tabla». Son operaciones distintas con consecuencias distintas: una deja la tabla vacía, la otra la hace desaparecer.
- Suponer que TRUNCATE siempre resetea los contadores. En PostgreSQL solo pasa si pedís RESTART IDENTITY.
- Pensar que TRUNCATE dispara los mismos triggers que DELETE. En MySQL, no dispara ON DELETE.
- Ejecutar DROP TABLE o TRUNCATE en MySQL sin backup previo. Es la única red de seguridad real cuando no hay ROLLBACK posible.
Preguntas frecuentes
¿TRUNCATE se puede revertir con ROLLBACK?
Depende del motor. En PostgreSQL, sí: TRUNCATE es transaccional, así que un ROLLBACK dentro de la misma transacción la deshace sin dejar rastro, igual que revertiría un UPDATE o un DELETE. En MySQL con InnoDB, no: TRUNCATE TABLE provoca un commit implícito, confirmando de forma automática cualquier transacción abierta en ese momento, incluida la que envolvía al TRUNCATE. Una vez que eso ocurre, no hay ROLLBACK que valga: lo truncado quedó confirmado, junto con cualquier otro cambio pendiente de esa misma transacción. En SQLite la pregunta no aplica igual porque no existe TRUNCATE: el vaciado se hace con un DELETE FROM sin condición, que sigue siendo DML y, como tal, reversible con ROLLBACK mientras no se confirme. Nunca asumas que un TRUNCATE es reversible sin confirmar antes qué motor estás usando.
¿Por qué DELETE es más lento si borran lo mismo?
Porque trabajan de maneras distintas por dentro, aunque el resultado visible parezca el mismo. DELETE recorre la tabla fila por fila: evalúa la condición del WHERE, dispara los triggers ON DELETE y registra la operación para poder deshacerla con ROLLBACK. Ese trabajo por fila tiene un costo que crece con la cantidad de filas. TRUNCATE y DROP TABLE operan a un nivel más alto: liberan páginas de almacenamiento completas o eliminan directamente los metadatos, sin la contabilidad que exige poder revertir cada fila ni disparar triggers. La documentación de MySQL lo dice de forma explícita: cuando no importa cuántas filas se borraron ni hace falta disparar triggers, TRUNCATE TABLE es más rápido que un DELETE sin WHERE. La diferencia se nota en tablas grandes. Esa velocidad no alcanza como único criterio: hay que sumarle la transaccionalidad, los contadores, las claves foráneas y los triggers.
¿Qué pasa con el AUTO_INCREMENT o la secuencia al truncar?
Depende del motor y de lo que le pidas explícitamente. En MySQL, TRUNCATE TABLE resetea el AUTO_INCREMENT: después de truncar, la próxima fila insertada vuelve a empezar desde el valor inicial, típicamente 1. En PostgreSQL el comportamiento por defecto es el contrario: TRUNCATE no reinicia las secuencias asociadas —CONTINUE IDENTITY, la opción implícita—, así que la próxima fila sigue la numeración donde había quedado. Para el mismo efecto que en MySQL hay que pedirlo con RESTART IDENTITY. En SQLite la pregunta no aplica igual porque no existe TRUNCATE: el vaciado se hace con DELETE FROM, y ese DELETE tampoco resetea contadores en ningún motor. Conviene comprobar el comportamiento por defecto del motor antes de asumir que vaciar una tabla también reinicia su numeración.
¿Puedo truncar una tabla si otra la referencia con una clave foránea?
En general no, salvo que lo indiques de forma explícita. Una clave foránea (FK, de Foreign Key) obliga a que un valor de una tabla exista también en otra, y vaciar de golpe la tabla referenciada dejaría huérfanas esas referencias. En MySQL con InnoDB, TRUNCATE TABLE falla si hay claves foráneas de otras tablas apuntando a la que querés vaciar. En PostgreSQL el comportamiento por defecto es equivalente: también falla, salvo que incluyas en el mismo TRUNCATE las tablas dependientes, o agregues CASCADE. Ojo con un matiz: en un TRUNCATE, CASCADE vacía también las tablas dependientes; en un DROP TABLE, en cambio, CASCADE elimina la restricción de clave foránea que dependía de la tabla eliminada, sin borrar ni vaciar la otra tabla. Confundir estos dos usos puede llevar a borrar datos que no querías tocar.
¿SQLite tiene TRUNCATE?
No, SQLite no incluye TRUNCATE en su lenguaje, a diferencia de PostgreSQL y de MySQL. Para vaciar por completo una tabla se usa DELETE FROM tabla sin ninguna cláusula WHERE. La documentación de SQLite aclara que este DELETE sin condición está optimizado internamente para no evaluar la condición fila por fila, acercándose en velocidad a un TRUNCATE. Pero sigue siendo una sentencia DML (Data Manipulation Language) transaccional: si se ejecuta dentro de una transacción sin confirmar, un ROLLBACK la revierte sin problema, algo que no se puede dar por sentado con un TRUNCATE en MySQL. Tampoco resetea ningún contador por vaciar la tabla. Si migrás scripts SQL entre motores y te encontrás con un TRUNCATE TABLE pensado para PostgreSQL o MySQL, hay que reescribirlo como DELETE FROM tabla para SQLite.
¿DROP TABLE dispara los triggers antes de borrar la tabla?
No dispara los triggers como si fuera un borrado de filas: los elimina junto con la tabla, sin ejecutarlos. Un trigger es un bloque de código que la base de datos ejecuta automáticamente ante un evento sobre una tabla, como un INSERT, un UPDATE o un DELETE, y pertenece a la tabla donde está definido. Al ejecutar un DROP TABLE se elimina la tabla completa —filas, estructura, índices y triggers—, así que no queda ningún objeto sobre el que puedan dispararse. Es distinto de TRUNCATE en MySQL, donde la tabla sigue existiendo vacía pero sus triggers ON DELETE no se activan; con DROP TABLE directamente no quedan triggers porque no queda tabla. En SQLite, el propio proceso interno de DROP TABLE borra primero los triggers y recién después aplica un DELETE implícito por claves foráneas.
Fuentes
Documentación oficial y material de la comunidad consultados para esta guía. Fecha de consulta: 29 de julio de 2026.
Aportes de la comunidad Underc0de
- Underc0de, foro. Resumen de SQL y su Sintaxis, por Mr. Bones, sección Base de Datos, 9 de agosto de 2023. Incluye la sintaxis de las tres sentencias, pero no compara su comportamiento entre motores: mención tangencial, no fuente de los hechos técnicos de esta guía. No hay en el foro ningún hilo que compare específicamente DELETE, TRUNCATE y DROP.
Documentación oficial
- PostgreSQL. DELETE. Sintaxis y comportamiento transaccional de la sentencia.
- PostgreSQL. TRUNCATE. Transaccionalidad, RESTART IDENTITY/CONTINUE IDENTITY y CASCADE.
- PostgreSQL. DROP TABLE. Comportamiento con dependencias y CASCADE.
- MySQL. DELETE Statement. Comportamiento fila por fila y triggers.
- MySQL. TRUNCATE TABLE Statement. Clasificación como DDL, commit implícito, AUTO_INCREMENT, claves foráneas y triggers.
- MySQL. DROP TABLE Statement. Eliminación de la tabla y sus dependientes.
- SQLite. DELETE. Optimización interna del DELETE sin WHERE, sin TRUNCATE en el lenguaje.
- SQLite. DROP TABLE. Eliminación de triggers asociados al eliminar la tabla.