"INSERT IGNORE" vs "INSERT ... ON DUPLICATE KEY UPDATE"

Mientras ejecuta un INSERT declaración con muchas filas, quiero omitir las entradas duplicadas que de otro modo causarían fallas. Después de algunas investigaciones, mis opciones parecen ser el uso de:

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

¿Estoy en lo cierto en estas suposiciones? ¿Cuál es la mejor manera de simplemente omitir las filas que podrían causar duplicados y simplemente continuar con las otras filas?

preguntado el 14 de febrero de 09 a las 03:02

12 Respuestas

Recomendaría usar INSERT...ON DUPLICATE KEY UPDATE.

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

  • Insertar una clave duplicada en columnas con PRIMARY KEY or UNIQUE restricciones
  • Insertar un NULL en una columna con un NOT NULL restricción.
  • Insertar una fila en una tabla particionada, pero los valores que inserta no se asignan a una partición.

Si utiliza REPLACE, MySQL en realidad hace un DELETE seguido de una INSERT internamente, lo que tiene algunos efectos secundarios inesperados:

  • Se asigna un nuevo ID de incremento automático.
  • Las filas dependientes con claves externas se pueden eliminar (si usa claves externas en cascada) o bien evitar la REPLACE.
  • Disparadores que disparan DELETE se ejecutan innecesariamente.
  • Los efectos secundarios también se propagan a las réplicas.

corrección: ambas REPLACE e INSERT...ON DUPLICATE KEY UPDATE son invenciones no estándar y propietarias específicas de MySQL. ANSI SQL 2003 define un MERGE declaración que puede resolver la misma necesidad (y más), pero MySQL no admite la MERGE .


Un usuario intentó editar esta publicación (la edición fue rechazada por los moderadores). La edición intentó agregar un reclamo que INSERT...ON DUPLICATE KEY UPDATE hace que se asigne una nueva identificación de incremento automático. Es cierto que la nueva identificación es generado, pero no se utiliza 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 por defecto):

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 declaración IODKU detecta el duplicado e invoca la actualización para cambiar el valor de u. Nota la AUTO_INCREMENT=3 indica que se generó una identificación, pero no se usó en la fila.

Mientras REPLACE elimina la fila original e inserta una nueva fila, generando e almacenar 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 |
+----+------+

Respondido 20 Oct 19, 17:10

En caso de que quiera ver qué significa todo esto, aquí hay un paso a paso 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 principal se basa en ambas columnas de esta tabla de referencia rápida. Una clave principal 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 adicional al establecer 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

Así que ahí lo tienes. Dado que todo esto se realizó en una mesa nueva casi sin datos y no en producción, los tiempos de ejecución fueron microscópicos e irrelevantes. Cualquiera con datos del mundo real sería más que bienvenido para contribuir.

Respondido 21 Oct 11, 19:10

Algo importante que agregar: cuando usa INSERT IGNORE y tiene violaciones clave, ¡MySQL NO genera una advertencia!

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

Query OK, 99 rows affected (0.04 sec)

Records: 100 Duplicates: 1 Warnings: 0

Como ve: ¡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".

Respondido el 20 de junio de 20 a las 10:06

Si está usando PHP, necesitará usar mysqli_affected_rows() para saber si el INSERT realmente sucedió. - Amal Murali

Con MySQL 5.5 y MariaDB 10 I do obtener un error Cannot add or update a child row: a foreign key constraint fails y sin filas (incluso los válidos) se agregan. - Floris

@Floris Ese error se debe a un restricción de clave externa y no debido a un llave duplicada. Estoy usando MySQL 5.5.28. Cuando usas INSERT IGNORE, las claves duplicadas se ignoran sin errores ni advertencias. - Toxalot

Yo uso rutinariamente INSERT IGNOREy suena exactamente como el tipo de comportamiento que estás buscando. Siempre que 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.

Respondido 14 Feb 09, 05:02

Como se mencionó anteriormente, si usa INSERT..IGNORE, los errores que ocurren 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 harían que la consulta se cancele si no se utiliza la palabra clave IGNORE).

Respondido el 07 de enero de 21 a las 17:01

Replace Parece una opción. O puede consultar con

IF NOT EXISTS(QUERY) Then INSERT

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

respondido 19 nov., 15:08

ON DUPLICATE KEY UPDATE no es realmente en el estándar. Es tan estándar como REPLACE. Ver FUSIÓN SQL.

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

Respondido 14 Feb 09, 05:02

Peligro potencial de INSERT IGNORE. Si está intentando insertar un valor VARCHAR más largo de lo que se definió la columna con, el valor se truncará y se insertará INCLUSO SI el modo estricto está habilitado.

Respondido 20 Oct 17, 00:10

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

respondido 13 mar '17, 14:03

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

Si desea insertar en la tabla y en el conflicto de la clave principal 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 ha visto anteriormente. Esta declaración de inserción intenta insertar una fila en la tabla1 con el valor de ayb en la columna columna1 y columna2 respectivamente.

Entendamos esta declaración en profundidad:

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

Ahora bien, si en la tabla1 no hay una fila que tenga el valor "a" en la columna1. Entonces, esta declaración insertará una fila en la tabla1.

Ahora bien, si en la tabla1 hay una fila que tiene el valor "a" en la columna2. 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, actualice esa fila en el conflicto de la clave principal o el índice único.
Leer más en este enlace.

Respondido el 13 de junio de 18 a las 16:06

Añadiendo a esto. Si utiliza ambas INSERT IGNORE e ON DUPLICATE KEY UPDATE en la misma declaración, la actualización seguirá ocurriendo si el inserto encuentra una clave duplicada. En otras palabras, la actualización tiene prioridad sobre el ignorado. Sin embargo, si el ON DUPLICATE KEY UPDATE la cláusula en sí misma causa un error de clave duplicada, ese error será ignorado.

Esto puede suceder si tiene más de una clave única o si su actualización intenta violar una restricción de clave externa.

CREATE TABLE test 
 (id BIGINT (20) UNSIGNED AUTO_INCREMENT, 
  str VARCHAR(20), 
  PRIMARY KEY(id), 
  UNIQUE(str));

INSERT INTO test (str) VALUES('A'),('B');

/* duplicate key error caused not by the insert, 
but by the update: */
INSERT INTO test (str) VALUES('B') 
 ON DUPLICATE KEY UPDATE str='A'; 

/* duplicate key error is suppressed */
INSERT IGNORE INTO test (str) VALUES('B') 
 ON DUPLICATE KEY UPDATE str='A';

Respondido 03 Jul 20, 23:07

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 yo se que col1 e col2 hacer un índice compuesto único.

Realiza un seguimiento del error, pero no lanza una excepción en el duplicado. En cuanto al rendimiento, la actualización por el mismo valor es eficiente como MySQL se da cuenta de esto 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.

respondido 30 nov., 19:19

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.