REPLACE

La sentencia REPLACE funciona como un INSERT, pero si la fila que intentas insertar duplica una clave PRIMARY o UNIQUE existente, MySQL elimina la fila antigua e inserta la nueva. Es equivalente a hacer un DELETE seguido de un INSERT en una sola operación atómica.

Sintaxis

REPLACE INTO tabla (columna1, columna2, ...)
VALUES (valor1, valor2, ...);

La sintaxis es idéntica a INSERT, solo cambia la palabra clave. También soporta las variantes con múltiples filas y con SELECT.

Ejemplo básico

Si intentamos insertar una etiqueta que ya existe para un producto:

REPLACE INTO etiquetas_producto (producto_id, etiqueta)
VALUES (1, 'premium');
Query OK, 2 rows affected (0.01 sec)

MySQL reporta "2 rows affected": una por la fila eliminada y otra por la fila insertada. Si la fila no existiera previamente, reportaría "1 row affected" (solo la inserción).

La diferencia crítica con INSERT ON DUPLICATE KEY UPDATE es que REPLACE elimina la fila antigua y crea una nueva. Esto significa que el ID de la fila cambia (si es AUTO_INCREMENT) y que se disparan los triggers de DELETE e INSERT.

REPLACE vs INSERT ON DUPLICATE KEY

Estas dos sentencias manejan duplicados de forma fundamentalmente diferente:

-- REPLACE: elimina la fila antigua, inserta una nueva
REPLACE INTO etiquetas_producto (producto_id, etiqueta)
VALUES (1, 'premium');
 
-- ON DUPLICATE KEY: actualiza la fila existente
INSERT INTO etiquetas_producto (producto_id, etiqueta)
VALUES (1, 'premium')
ON DUPLICATE KEY UPDATE etiqueta = 'premium';

REPLACE elimina y crea: la fila tiene un nuevo ID (si es AUTO_INCREMENT), se pierden los valores de columnas no especificadas (se reemplazan por sus defaults), se ejecutan triggers de DELETE + INSERT, y los CASCADE de claves foráneas se activan para el DELETE.

ON DUPLICATE KEY UPDATE modifica la fila existente: el ID se mantiene, las columnas no mencionadas conservan su valor, se ejecuta un trigger de UPDATE, y no se activan cascadas de DELETE.

El problema del CASCADE

El mayor peligro de REPLACE es su interacción con ON DELETE CASCADE. Si la tabla tiene claves foráneas con CASCADE, el DELETE implícito de REPLACE eliminará registros en las tablas relacionadas:

-- PELIGROSO si la tabla tiene dependencias con CASCADE
REPLACE INTO productos (id, nombre, precio, stock, categoria_id)
VALUES (1, 'iPhone 15 Pro Max', 1499.99, 50, 6);

Si el producto 1 tiene reseñas (con ON DELETE CASCADE), el REPLACE eliminaría primero el producto junto con todas sus reseñas, y después insertaría el nuevo producto. Las reseñas se perderían permanentemente.

Con INSERT ON DUPLICATE KEY UPDATE, esto no pasa porque la fila no se elimina.

REPLACE con múltiples filas

REPLACE INTO etiquetas_producto (producto_id, etiqueta) VALUES
    (2, 'oferta'),
    (2, 'destacado'),
    (3, 'recomendado');

Cada fila se evalúa individualmente. Si ya existe, se elimina y se inserta de nuevo. Si no existe, se inserta directamente.

REPLACE INTO ... SELECT

También puedes usar REPLACE con un SELECT como fuente de datos:

REPLACE INTO etiquetas_producto (producto_id, etiqueta)
SELECT producto_id, 'en stock'
FROM productos
WHERE stock > 100;

Para cada producto con más de 100 unidades en stock, se inserta (o reemplaza) la etiqueta 'en stock'.

Cuándo usar REPLACE

En la práctica, REPLACE tiene pocos casos de uso donde sea la mejor opción. INSERT ON DUPLICATE KEY UPDATE es casi siempre preferible porque:

No elimina la fila (evita problemas con CASCADE y triggers). Mantiene el ID original de la fila. Permite actualizar solo algunas columnas sin afectar las demás. Es más predecible y menos propenso a efectos secundarios.

Los pocos casos donde REPLACE tiene sentido son tablas simples sin claves foráneas donde quieres reemplazar completamente una fila (por ejemplo, tablas de configuración o de caché). Si la tabla tiene relaciones con otras tablas, evita REPLACE y usa ON DUPLICATE KEY UPDATE.

En el siguiente artículo veremos TRUNCATE TABLE, la forma más eficiente de vaciar completamente una tabla.

Escrito por Eduardo Lázaro