UPDATE modifica filas existentes y DELETE las elimina. Ambas instrucciones son peligrosas sin cláusula WHERE: un UPDATE o DELETE sin WHERE afecta a todas las filas de la tabla, y en modo autocommit (que es el predeterminado) no hay deshacer. La regla de oro es siempre probar el WHERE con un SELECT antes de ejecutar la modificación.
En este capítulo cubrimos UPDATE y DELETE con ejemplos prácticos, TRUNCATE para vaciar tablas, y el patrón de soft delete para producción.
UPDATE básico
-- Actualizar un campo (SIEMPRE con WHERE) UPDATE productos SET precio = 54.99 WHERE id = 1; -- Actualizar varios campos a la vez UPDATE clientes SET ciudad = 'Bilbao', nombre = 'Ana Pérez' WHERE id = 1; -- Actualizar con expresión matemática UPDATE productos SET precio = ROUND(precio * 1.05, 2); -- subir 5% a todos UPDATE productos SET stock = stock - 1 WHERE id = 2; -- decrementar stock -- Antes de ejecutar el UPDATE, verificar qué filas afecta: SELECT id, nombre, precio FROM productos WHERE id_categoria = 1; -- Si el resultado es el esperado, ejecutar: UPDATE productos SET precio = ROUND(precio * 1.05, 2) WHERE id_categoria = 1;
UPDATE con JOIN (MySQL)
-- Aplicar 5% de descuento a pedidos de clientes de Madrid UPDATE pedidos p JOIN clientes c ON c.id = p.id_cliente SET p.total = ROUND(p.total * 0.95, 2) WHERE c.ciudad = 'Madrid' AND p.estado = 'pendiente'; -- Actualizar el campo 'activo' de clientes que no han pedido nada en el último año UPDATE clientes c LEFT JOIN pedidos p ON p.id_cliente = c.id AND p.fecha >= NOW() - INTERVAL 1 YEAR SET c.activo = FALSE WHERE p.id IS NULL;
DELETE básico
-- Eliminar una fila por ID DELETE FROM clientes WHERE id = 42; -- Eliminar con condición compuesta DELETE FROM pedidos WHERE estado = 'cancelado' AND fecha < '2024-01-01'; -- Verificar antes: SELECT id, estado, fecha FROM pedidos WHERE estado = 'cancelado' AND fecha < '2024-01-01'; -- Si es lo que esperas, ejecutar el DELETE
DELETE con JOIN (MySQL)
-- Borrar las líneas de pedido de todos los pedidos cancelados DELETE lp FROM lineas_pedido lp JOIN pedidos p ON p.id = lp.id_pedido WHERE p.estado = 'cancelado'; -- Borrar tanto los pedidos como sus líneas en una sola instrucción DELETE p, lp FROM pedidos p JOIN lineas_pedido lp ON lp.id_pedido = p.id WHERE p.estado = 'cancelado' AND p.fecha < '2024-01-01';
TRUNCATE: vaciar una tabla
-- Elimina TODAS las filas y resetea AUTO_INCREMENT a 1 -- Mucho más rápido que DELETE sin WHERE en tablas grandes TRUNCATE TABLE lineas_pedido; -- Diferencias con DELETE: -- TRUNCATE no se puede usar dentro de una transacción en MySQL (es DDL) -- TRUNCATE no dispara triggers ON DELETE -- TRUNCATE no puede hacerse si hay FKs apuntando a la tabla (con ON DELETE RESTRICT)
Soft delete: el patrón para producción
En producción rara vez se borra físicamente. Lo habitual es marcar el registro como eliminado con una columna deleted_at:
-- Añadir la columna a tablas existentes ALTER TABLE clientes ADD COLUMN deleted_at DATETIME NULL DEFAULT NULL; -- Soft delete: no borra, solo marca UPDATE clientes SET deleted_at = NOW() WHERE id = 42; -- Todas las consultas deben filtrar los eliminados SELECT * FROM clientes WHERE deleted_at IS NULL; -- Recuperar un cliente eliminado UPDATE clientes SET deleted_at = NULL WHERE id = 42; -- Ver historial completo (incluyendo eliminados) SELECT *, IF(deleted_at IS NOT NULL, 'eliminado', 'activo') AS estado FROM clientes ORDER BY id;
La ventaja del soft delete es la trazabilidad y la posibilidad de recuperación. La desventaja es que todas las consultas deben incluir WHERE deleted_at IS NULL, lo que puede olvidarse y provocar que aparezcan datos eliminados.
