¿Cuáles son las diferencias prácticas entre `REPLACE` e` INSERT ... ON DUPLICATE KEY UPDATE` en MySQL?

81

Lo que necesito es establecer los valores de todos los campos de un registro con una clave en particular (la clave es compuesta en realidad), insertando el registro si todavía no hay ningún registro con dicha clave.

REPLACEparece destinado a hacer el trabajo, pero al mismo tiempo sugiere su página de manual INSERT ... ON DUPLICATE KEY UPDATE.

¿Cuáles de ellos debería elegir mejor y por qué?

El único "efecto secundario" de REPLACEeso que me viene a la mente es que incrementaría los valores de autoincremento (afortunadamente no uso ninguno) mientras que INSERT ... ON DUPLICATE KEY UPDATEprobablemente no lo haría. ¿Cuáles son las otras diferencias prácticas a tener en cuenta? ¿En qué casos particulares se REPLACEpuede preferir INSERT ... ON DUPLICATE KEY UPDATEy viceversa?

Iván
fuente
INSERT ... ON DUPLICATE KEY UPDATE de hecho también incrementa el contador de autoincremento. No para el registro que se actualiza, sino para el siguiente registro insertado. Entonces, si el ID más alto es 10 y hace una inserción duplicada, y luego inserta un nuevo valor único, el ID de esa fila se convertirá en 12.
marlar

Respuestas:

117

REPLACEinternamente realiza una eliminación y luego una inserción. Esto puede causar problemas si tiene una restricción de clave externa que apunta a esa fila. En esta situación, REPLACEpodría fallar o peor: si su clave externa está configurada para eliminar en cascada, REPLACEhará que las filas de otras tablas se eliminen. Esto puede suceder incluso si la restricción se cumplió tanto antes como después de la REPLACEoperación.

El uso INSERT ... ON DUPLICATE KEY UPDATEevita este problema y, por lo tanto, es preferible.

Mark Byers
fuente
1
Buena respuesta, pero en el caso mío, este problema no se resolverá. Sin embargo, la posibilidad de colisión puede considerarse 50/50. ¿Qué debo elegir entonces? Y como INSERT ... ON DUPLICATE KEY UPDATEparece considerablemente "mejor", ¿en qué casos particulares puede "REEMPLAZAR" ser una mejor opción?
Iván
3
He investigado un poco y, por lo que puedo decir, no hay una razón común para usar REPLACE en lugar de INSERT ... ON DUPLICATE KEY UPDATE. Es esencialmente una característica heredada. A menos que haya alguna razón en particular por la que su código se base en que las filas se eliminen y vuelvan a agregar, con los efectos asociados en los índices y los valores de incremento automático, no parece haber ninguna razón para usarlo.
Nathan Stretch
2
On REPLACEactualizará su valor de incremento automático de PK si hace un DELETEy INSERT. Que es exactamente lo que quiero. No quiero que el consumidor encuentre el registro bajo el mismo PK, por lo que no obtiene filas. Cuando quiero que lo encuentren (actualización real), usoUPDATE
radtek
Por lo que la otra mitad de la pregunta: ¿Cuándo prefiere REPLACEmás INSERT ... ON DUPLICATE KEY UPDATE? ¿Por qué se preferiría un INSERT+ DELETEsobre un UPDATE?
LemonPi
59

Para responder a la pregunta en términos de rendimiento, hice una prueba usando ambos métodos

Reemplazar en implica:
1. Intente insertar en la tabla
2. Si 1 falla, elimine la fila e inserte una nueva fila

Insertar en clave duplicada La actualización implica:
1.Intentar insertar en la tabla
2.Si 1 falla, actualizar fila

Si todos los pasos involucrados son insertos, no debería haber diferencia en el rendimiento. La velocidad tiene que depender del número de actualizaciones involucradas. El peor de los casos es cuando todas las declaraciones son actualizaciones.

Probé ambas declaraciones en mi tabla InnoDB que involucran 62,510 entradas (solo actualizaciones). En velocidades de campaña:
Reemplazar en: 77.411 segundos
Insertar en clave duplicada Actualización: 2.446 segundos

Insert on Duplicate Key update is almost 32 times faster.

Tamaño de la tabla: 1.249.250 filas con 12 columnas en un Amazon m3.

Katrix
fuente
Estadísticas geniales, ¿lo intentaste Insert on Duplicate Key Replace? ¿Fue más lento?
radtek
@radtek solo puedes escribir ON DUPLICATE KEY UPDATE, no puedes escribir ON DUPLICATE KEY REPLACE. Si desea actualizar todos los valores de la fila existente al duplicar la clave, debe escribir ON DUPLICATE KEY UPDATE col1=VALUES(col1), col2=VALUES(col2), ...: debe enumerar todas las columnas manualmente.
izogfif
Sé que solo preguntaba qué era más rápido y parece que la actualización es.
radtek
9

Cuando utilizo en REPLACElugar de INSERT ... ON DUPLICATE KEY UPDATE, a veces observo problemas de bloqueo o interbloqueo de teclas cuando llegan varias consultas rápidamente para una clave determinada. La atomicidad de este último (además de no causar eliminaciones en cascada) es una razón de más para usarlo.

Andrew Mao
fuente
3

Si no enumera todas las columnas, creo REPLACEque restablecerá las columnas no mencionadas con sus valores predeterminados en las filas reemplazadas. ON DUPLICATE KEY UPDATEdejará las columnas no mencionadas sin cambios.

Barmar
fuente
3

¿En qué casos particulares se puede preferir REPLACE sobre INSERT ... ON DUPLICATE KEY UPDATE y viceversa?

Acabo de descubrir por las malas que, en el caso de tablas con un motor de almacenamiento FEDERADO, las INSERT...ON DUPLICATE KEY UPDATEdeclaraciones se aceptan, pero fallan (con un Error 1022: No se puede escribir; clave duplicada en la tabla ...) si hay una clave duplicada se produce una infracción: consulte el punto correspondiente en esta página del Manual de referencia de MySQL.

Afortunadamente, pude usar en REPLACElugar de INSERT...ON DUPLICATE KEY UPDATEdentro de mi activador de inserción posterior para lograr el resultado deseado de replicar los cambios en una tabla FEDERADA.

w5m
fuente
2

Reemplazar parece que hace dos operaciones en el caso de que la clave ya exista. ¿Quizás eso implica que hay una diferencia de velocidad entre los dos?

(INSERTAR) una actualización frente a una eliminación + una inserción (REEMPLAZAR)

EDITAR: Mi implicación de que el reemplazo podría ser más lento es en realidad completamente incorrecta. Bueno, de acuerdo con esta publicación de blog de todos modos ... http://www.tokutek.com/2010/07/why-insert-on-duplicate-key-update-may-be-slow-by-incurring-disk-seeks /

Isaac Fife
fuente
0

REPLACE parece ser necesario a veces porque INSERT IGNORE no parece funcionar con transformaciones de datos.

Si hago esto, solo configuro largerCityPop a sí mismo:

INSERTE IGNORAR EN LAS ciudades más grandes (stateID, most

Si hago esto, estoy usando la función GRUPO incorrectamente:

INSERTAR IGNORE INTO largerCities (stateID, largerCityPop, statePop) SELECT stateID, MAX (city.pop) as maximumCityPop, state.pop FROM city UNETE al estado en city.stateID = state.ID GROUP BY city.stateID ON DUPLICATE KEY ACTUALIZAR largerCityPop = MAX (ciudad.pop)

Y si hago esto, MySQL no reconocerá el nombre de la columna:

INSERTAR IGNORE INTO largerCities (stateID, largerCityPop, statePop) SELECCIONE stateID, MAX (city.pop) como más grandeCityPop, state.pop FROM city UNIR al estado en city.stateID = state.ID GROUP BY city.stateID ON DUPLICATE KEY ACTUALIZAR largerCityPop = ciudad .largestCityPop

Esto funciona, pero parece simplemente feo:

INSERTAR IGNORE INTO largerCities (stateID, largerCityPop, statePop) SELECT * FROM (SELECT stateID, MAX (city.pop) as BigCityPop, state.pop FROM city JOIN state on city.stateID = state.ID GROUP BY city.stateID) x ON ACTUALIZACIÓN DE LA CLAVE DUPLICADA largerCityPop = BigCityPop

Dan Marsh
fuente
Atención: la INSERT IGNOREconsulta finalizará correctamente (y emitirá una advertencia) si falla una restricción externa . Si desea detectar un error como este, mejor use ON DUPLICATE KEY UPDATEsin IGNORE.
izogfif