Saltar al contenido

Mentoring

INSERT ... ON DUPLICATE KEY UPDATE en MySQL

Aprende a insertar o actualizar un registro en MySQL usando INSERT ... ON DUPLICATE KEY UPDATE.

Mentoring 4 min de lectura

Actualizado el

INSERT OR UPDATE en MySQL

Un upsert —insert + update— intenta insertar una fila y, si una PRIMARY KEY o una restricción UNIQUE produce un duplicado, actualiza la fila existente.

En MySQL, la forma de hacer un upsert es INSERT ... ON DUPLICATE KEY UPDATE.

Requisito: clave primaria o índice único

Para que la sentencia detecte el duplicado, la tabla debe tener una clave primaria o un índice único sobre las columnas que se repiten. Si no existe ninguna de las dos, el INSERT siempre añade una fila nueva. Consulta cómo elegir y crear índices en MySQL.

Sintaxis actual

INSERT INTO tabla (columna1, columna2)
VALUES (valor1, valor2) AS nuevo
ON DUPLICATE KEY UPDATE
  columna1 = nuevo.columna1,
  columna2 = nuevo.columna2;

El alias de fila (nuevo en el ejemplo) representa la fila que se intentó insertar y permite referirse a sus valores dentro de la cláusula de actualización. Con esta forma ya no se usa VALUES(columna), que está deprecada desde MySQL 8.0.20 y no debe recomendarse en código nuevo.

Ejemplo básico

INSERT INTO usuarios (id, nombre, email)
VALUES (1, 'Juan Pérez', '[email protected]') AS nuevo
ON DUPLICATE KEY UPDATE
  nombre = nuevo.nombre,
  email = nuevo.email;

Si no existe un usuario con id = 1, lo crea. Si ya existe, actualiza nombre y email.

Ejemplo: actualizar inventario

INSERT INTO inventario (producto_id, cantidad)
VALUES (1, 10) AS nuevo
ON DUPLICATE KEY UPDATE
  cantidad = inventario.cantidad + nuevo.cantidad;

Si el producto no existe en inventario, lo inserta con cantidad 10. Si ya existe, suma 10 a la cantidad actual.

Ejemplo: guardar la puntuación más alta

INSERT INTO puntuaciones (jugador_id, puntuacion)
VALUES (1, 500) AS nueva
ON DUPLICATE KEY UPDATE
  puntuacion = GREATEST(puntuaciones.puntuacion, nueva.puntuacion);

Solo actualiza la puntuación si la nueva es mayor que la guardada.

Diferencia con REPLACE INTO

REPLACE INTO también resuelve los duplicados, pero lo hace de forma destructiva: elimina la fila en conflicto y después inserta una nueva.

REPLACE INTO usuarios (id, nombre, email)
VALUES (1, 'Juan Pérez', '[email protected]');

Como la fila anterior se borra, REPLACE puede perder los valores que no se hayan indicado en la sentencia y activa los efectos propios de un borrado seguido de una inserción. ON DUPLICATE KEY UPDATE conserva la fila y modifica solo las columnas indicadas.

Cuándo usarlo

Usa INSERT ... ON DUPLICATE KEY UPDATE cuando quieras mantener el mismo registro y actualizar solo algunos campos. Si únicamente necesitas añadir filas nuevas, empieza por la guía general de INSERT en MySQL.

En cualquier caso, usa consultas parametrizadas desde tu aplicación para evitar inyección SQL.

Preguntas frecuentes

¿Desde qué versión de MySQL funciona el alias de fila?

El alias de fila se admite desde MySQL 8.0.19. Es distinto de VALUES(columna), que está deprecado desde MySQL 8.0.20.

¿Qué ocurre si hay varias claves únicas?

Si la inserción entra en conflicto con varias claves únicas, la sentencia actualiza una fila, no todas las coincidentes. Evítala si la elección puede ser ambigua. La actualización también puede fallar si provoca otro conflicto de clave única.

¿Cuántas filas afectadas devuelve?

Por cada fila, MySQL informa 1 si se inserta, 2 si se actualiza y 0 si ya tenía los mismos valores. En el C API, CLIENT_FOUND_ROWS puede hacer que el caso sin cambios informe 1.

¿Puedo insertar o actualizar varias filas a la vez?

Sí. Si email tiene una restricción UNIQUE, puedes pasar varias tuplas y usar un alias de fila (disponible desde MySQL 8.0.19):

INSERT INTO usuarios (nombre, email)
VALUES
  ('Ana', '[email protected]'),
  ('Luis', '[email protected]') AS nueva
ON DUPLICATE KEY UPDATE
  nombre = nueva.nombre;

Cada tupla se inserta si no entra en conflicto o actualiza la fila existente si choca con una clave primaria o una restricción UNIQUE.