"INSERT IGNORE" vs "INSERT ... ON DUPLICATE KEY UPDATE"
Frecuentes
Visto 896,725 equipos
865
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, oINSERT 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?
12 Respuestas
1025
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
orUNIQUE
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
180
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
42
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
21
Yo uso rutinariamente INSERT IGNORE
y 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
21
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
9
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
7
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
6
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
4
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
2
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
2
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
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 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 mysql insert or haz tu propia pregunta.
Si está usando PHP, necesitará usar
mysqli_affected_rows()
para saber si elINSERT
realmente sucedió. - Amal MuraliCon 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