"INSERTAR IGNORE" vs "INSERTAR ... EN LA ACTUALIZACIÓN DE LA CLAVE DUPLICADA"

833

Mientras ejecuta un INSERT declaración con muchas filas, quiero omitir entradas duplicadas que de lo contrario causarían un error. Después de investigar un poco, mis opciones parecen ser el uso de:

  • ON DUPLICATE KEY UPDATE lo que implica una actualización innecesaria a algún costo, o
  • INSERT IGNORE lo que implica una invitación para que otros tipos de fallas entren sin previo aviso.

¿Estoy en lo cierto en estos supuestos? ¿Cuál es la mejor manera de simplemente omitir las filas que pueden causar duplicados y simplemente continuar con las otras filas?

Thomas G Henry
fuente

Respuestas:

991

Yo recomendaría usar INSERT...ON DUPLICATE KEY UPDATE.

Si lo usa INSERT IGNORE, la fila no se insertará realmente si da como resultado una clave duplicada. Pero la declaración no generará un error. Genera una advertencia en su lugar. Estos casos incluyen:

  • Insertar una clave duplicada en columnas con PRIMARY KEYo UNIQUErestricciones.
  • Insertar un NULL en una columna con una NOT NULLrestricción.
  • Insertar una fila en una tabla particionada, pero los valores que inserte no se asignan a una partición.

Si lo usa REPLACE, MySQL en realidad hace un DELETEseguimiento INSERTinterno, que tiene algunos efectos secundarios inesperados:

  • Se asigna una nueva ID de incremento automático.
  • Las filas dependientes con claves foráneas pueden eliminarse (si utiliza claves foráneas en cascada) o, de lo contrario, evitar REPLACE .
  • Los disparadores que disparan DELETEse ejecutan innecesariamente.
  • Los efectos secundarios también se propagan a las réplicas.

Corrección: tanto REPLACEy INSERT...ON DUPLICATE KEY UPDATEno son estándar, las invenciones de propiedad específica a MySQL. ANSI SQL 2003 define una MERGEdeclaración que puede resolver la misma necesidad (y más), pero MySQL no admite la MERGEdeclaración.


Un usuario intentó editar esta publicación (los moderadores rechazaron la edición). La edición intentó agregar un reclamo que INSERT...ON DUPLICATE KEY UPDATEhace que se asigne una nueva identificación de incremento automático. Es cierto que se genera la nueva identificación , pero no se usa en la fila modificada.

Vea la demostración a continuación, probada con Percona Server 5.5.28. La variable de configuración innodb_autoinc_lock_mode=1(el valor predeterminado):

mysql> create table foo (id serial primary key, u int, unique key (u));
mysql> insert into foo (u) values (10);
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   10 |
+----+------+

mysql> show create table foo\G
CREATE TABLE `foo` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `u` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `u` (`u`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1

mysql> insert into foo (u) values (10) on duplicate key update u = 20;
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   20 |
+----+------+

mysql> show create table foo\G
CREATE TABLE `foo` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `u` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `u` (`u`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1

Lo anterior demuestra que la instrucción IODKU detecta el duplicado e invoca la actualización para cambiar el valor de u. Tenga en cuenta que AUTO_INCREMENT=3indica que se generó una identificación, pero que no se utilizó en la fila.

Mientras REPLACEque elimina la fila original e inserta una nueva fila, generando y almacenando una nueva identificación de incremento automático:

mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   20 |
+----+------+
mysql> replace into foo (u) values (20);
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  3 |   20 |
+----+------+
Bill Karwin
fuente
3
Me pregunto si el equipo de desarrollo de MySQL tiene alguna intención de adoptar MERGE de ANSI SQL 2003.
Lonnie Best
1
@LonnieBest: La solicitud de función para implementar MERGE se realizó en 2005, pero, hasta donde sé, no hay progreso ni plan. bugs.mysql.com/bug.php?id=9018
Bill Karwin
2
Ah, puedo agregar que genera advertencias (no errores) por falta de coincidencia de tipos no válidos, pero no genera una advertencia para la clave primaria compuesta duplicada.
Fabrício Matté
11
Acabo de mirar una tabla que ha sido poblada por muchas INSERT ... ON DUPLICATE KEY UPDATE ...declaraciones. Muchos de los datos están duplicados, y ha dado como resultado que una instancia de AI PK aumente de 17,029,941 a 46,271,740 entre dos filas. Esa generación de una nueva IA cada vez significa que su rango puede llenarse muy rápidamente y necesita limpiar. ¡Esta mesa tiene solo dos semanas!
Engineer81
44
@AntTheKnee, ahh, los desafíos de trabajar en tiempos de Big Data.
Bill Karwin
174

En caso de que quiera ver qué significa todo esto, aquí hay un golpe de golpe de todo:

CREATE TABLE `users_partners` (
  `uid` int(11) NOT NULL DEFAULT '0',
  `pid` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`uid`,`pid`),
  KEY `partner_user` (`pid`,`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

La clave primaria se basa en ambas columnas de esta tabla de referencia rápida. Una clave primaria requiere valores únicos.

Vamos a empezar:

INSERT INTO users_partners (uid,pid) VALUES (1,1);
...1 row(s) affected

INSERT INTO users_partners (uid,pid) VALUES (1,1);
...Error Code : 1062
...Duplicate entry '1-1' for key 'PRIMARY'

INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1);
...0 row(s) affected

INSERT INTO users_partners (uid,pid) VALUES (1,1) ON DUPLICATE KEY UPDATE uid=uid
...0 row(s) affected

tenga en cuenta que lo anterior ahorró demasiado trabajo extra al configurar la columna igual a sí misma, no se necesita ninguna actualización

REPLACE INTO users_partners (uid,pid) VALUES (1,1)
...2 row(s) affected

y ahora algunas pruebas de varias filas:

INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...Error Code : 1062
...Duplicate entry '1-1' for key 'PRIMARY'

INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...3 row(s) affected

no se generaron otros mensajes en la consola, y ahora tiene esos 4 valores en los datos de la tabla. Eliminé todo excepto (1,1) para poder probar desde el mismo campo de juego

INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4) ON DUPLICATE KEY UPDATE uid=uid
...3 row(s) affected

REPLACE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...5 row(s) affected

Entonces ahí lo tienes. Como todo esto se realizó en una tabla nueva con casi ningún dato y no en producción, los tiempos de ejecución fueron microscópicos e irrelevantes. Cualquier persona con datos del mundo real sería más que bienvenido a contribuir.

Paulus Maximus
fuente
Corrí ambos en clave duplicada y reemplazar en. Mis tablas terminaron con ~ 120K filas con aproximadamente el 30% de mis filas duplicadas. En clave duplicada se ejecutó en 102 segundos y reemplazar se ejecutó en 105 segundos. Para mi caso, me quedo con la clave duplicada.
crunkchitis
1
Probé lo anterior con MariaDB 10 y recibí una advertencia al ejecutar INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4).
Floris
¿Qué versión de MySQL usaste para todo esto?
Radu Murzea
41

Algo importante para agregar: ¡Al usar INSERT IGNORE y usted tiene violaciones clave, MySQL NO genera una advertencia!

Si intenta, por ejemplo, insertar 100 registros a la vez, con uno defectuoso, entraría en modo interactivo:

Query OK, 99 rows affected (0.04 sec)

Records: 100 Duplicates: 1 Warnings: 0

Como ves: ¡Sin advertencias! Este comportamiento incluso se describe erróneamente en la documentación oficial de Mysql.

Si su script necesita ser informado, si algunos registros no se han agregado (debido a violaciones de claves), debe llamar a mysql_info () y analizarlo para obtener el valor "Duplicados".

Jens
fuente
66
Si está usando PHP, deberá usarlo mysqli_affected_rows()para saber si INSERTrealmente sucedió.
Amal Murali
Con tanto MySQL 5.5 y 10 MariaDB me hago obtener un error Cannot add or update a child row: a foreign key constraint fails y no hay filas se añaden (incluso los válidos).
Floris
2
@Floris Ese error se debe a una restricción de clave externa y no a una clave duplicada . Estoy usando MySQL 5.5.28. Cuando se usa INSERT IGNORE, las claves duplicadas se ignoran sin error ni advertencia.
toxalot
20

Yo uso habitualmente INSERT IGNORE, y también parece exactamente el tipo de comportamiento que estás buscando. Siempre y cuando sepa que las filas que causarían conflictos de índice no se insertarán y planifique su programa en consecuencia, no debería causar ningún problema.

David Z
fuente
44
Me preocupa ignorar otros errores además de la duplicación. ¿Es correcto o INSERT IGNORE solo ignora solo ignora la falla de duplicación? ¡Gracias!
Thomas G Henry
2
Convierte cualquier error en una advertencia. Vea una lista de tales casos en mi respuesta.
Bill Karwin
Es una pena; Desearía que solo ignorara las fallas duplicadas.
Lonnie Best
¡Las violaciones clave causan errores ! Vea mi comentario en la respuesta de @Jens.
Floris
1
@Pacerier, depende de si su aplicación busca advertencias. O si puede verificar las advertencias. Por ejemplo, la mayoría de los paquetes ORM no le dan la oportunidad. Algunos conectores (por ejemplo, JDBC) también lo separan de la API MySQL para que no tenga la oportunidad de verificar las advertencias.
Bill Karwin
18

Sé que esto es antiguo, pero agregaré esta nota en caso de que alguien más (como yo) llegue a esta página mientras trato de encontrar información sobre INSERTAR .. REGISTRARSE.

Como se mencionó anteriormente, si usa INSERT..IGNORE, los errores que se producen al ejecutar la instrucción INSERT se tratan como advertencias.

Una cosa que no se menciona explícitamente es que INSERT..IGNORE hará que los valores no válidos se ajusten a los valores más cercanos cuando se inserten (mientras que los valores no válidos causarían que la consulta se cancelara si no se usaba la palabra clave IGNORE).

Chris
fuente
66
No estoy realmente seguro de lo que quieres decir con "valores no válidos" y corregido a qué. ¿Podría dar un ejemplo o una explicación más detallada?
Marenz
44
Significa que si inserta el tipo de datos incorrecto en un campo cuando usa "INSERT IGNORE", los datos se modificarán para que coincidan con el tipo de datos del campo y se insertará un valor potencialmente no válido, entonces la consulta continuará ejecutándose. Solo con "INSERTAR", se generará un error sobre el tipo de datos incorrecto y la consulta se anulará. Esto podría estar bien si se inserta un número en un campo varchar o de texto, pero insertar una cadena de texto en un campo con un tipo de datos numéricos daría como resultado datos incorrectos.
codewaggle
2
@Marenz otro ejemplo: si su tabla tiene una columna no nula y su consulta "INSERT IGNORE" no especifica un valor para esa columna, la fila se insertará con un valor cero en esa columna, independientemente de si sql_mode estricto está habilitado .
Shannon
Buen punto sobre valores no válidos! Este hilo es ideal para aprender sobre "INSERTAR IGNORE", también dejaré mis 5 centavos: medium.com/legacy-systems-diary/... bonito artículo con los ejemplos sobre cuán cuidadoso debe ser al usar "INSERT IGNORE" declaración.
0x49D1
8

ON DUPLICATE KEY UPDATE no está realmente en el estándar. Es casi tan estándar como REPLACE. Ver SQL MERGE .

Esencialmente, ambos comandos son versiones de sintaxis alternativa de los comandos estándar.

Chris KL
fuente
1
Reemplazar elimina e inserta, mientras que la actualización de la clave onduplicate actualiza la fila existente. algunas diferencias son: identificación de incremento automático, posición de fila, un montón de disparadores
ahnbizcad
8

ReplaceEn parece una opción. O puedes consultar con

IF NOT EXISTS(QUERY) Then INSERT

Esto insertará o eliminará y luego insertará. Tiendo a ir por un IF NOT EXISTScheque primero.

IEnumerator
fuente
Gracias por la rápida respuesta. Asumo por todas partes, pero supongo que esto sería similar a ON DUPLICATE KEY UPDATE en que realizaría actualizaciones innecesarias. Parece un desperdicio, pero no estoy seguro. Cualquiera de estos debería funcionar. Me pregunto si alguien sabe cuál es el mejor.
Thomas G Henry
66
NTuplip: esa solución aún está abierta a las condiciones de carrera a partir de inserciones por transacciones concurrentes.
Chris KL
REPLACEborra todas las filas de la tabla con cualquier tecla PRIMARYo coincidente UNIQUE, entonces INSERTs . Potencialmente, esto es mucho más trabajo que IODKU.
Rick James
4

Posible peligro de INSERTAR IGNORE. Si está intentando insertar el valor VARCHAR durante más tiempo, la columna se definió con: el valor se truncará y se insertará INCLUSO SI el modo estricto está habilitado.

Jajaja
fuente
3

Si utiliza insert ignoretener una SHOW WARNINGS;declaración al final de su conjunto de consultas, se mostrará una tabla con todas las advertencias, incluidos los ID que fueron duplicados.

Ray Foss
fuente
SHOW WARNINGS;solo parece afectar la última consulta. Las declaraciones anteriores no se acumulan, si tiene más de una declaración.
Kawu
2

Si desea insertar en la tabla y en el conflicto de la clave primaria o índice único, actualizará la fila en conflicto en lugar de insertar esa fila.

Sintaxis:

insert into table1 set column1 = a, column2 = b on duplicate update column2 = c;

Ahora aquí, esta declaración de inserción puede verse diferente a lo que has visto anteriormente. Esta instrucción de inserción intenta insertar una fila en la tabla1 con el valor de ayb en la columna columna1 y columna2 respectivamente.

Comprendamos esta declaración en profundidad:

Por ejemplo: aquí la columna1 se define como la clave principal en la tabla1.

Ahora, si en la tabla 1 no hay una fila que tenga el valor "a" en la columna 1. Entonces, esta instrucción insertará una fila en la tabla1.

Ahora, si en la tabla 1 hay una fila que tiene el valor "a" en la columna 2. Entonces, esta declaración actualizará el valor de la columna2 de la fila con "c" donde el valor de la columna1 es "a".

Entonces, si desea insertar una nueva fila, de lo contrario, actualice esa fila en el conflicto de la clave primaria o el índice único.
Lea más en este enlace

Dilraj Singh
fuente
0

INSERT...ON DUPLICATE KEY UPDATE se prefiere para evitar la gestión de excepciones inesperadas.

Esta solución funciona cuando tiene ** 1 restricción única ** solamente

En mi caso, lo sé col1y col2hago un índice compuesto único.

Realiza un seguimiento del error, pero no arroja una excepción en duplicado. En cuanto al rendimiento, la actualización por el mismo valor es eficiente ya que MySQL lo nota y no lo actualiza.

INSERT INTO table
  (col1, col2, col3, col4)
VALUES
  (?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
    col1 = VALUES(col1),
    col2 = VALUES(col2)

La idea de utilizar este enfoque surgió de los comentarios en phpdelusions.net/pdo .

micaball
fuente