En la actualización de clave duplicada igual que insertar

158

He buscado pero no he encontrado si es posible.

Tengo esta consulta MySQL:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)

La identificación de campo tiene un "índice único", por lo que no puede haber dos. Ahora, si la misma identificación ya está presente en la base de datos, me gustaría actualizarla. Pero, ¿realmente tengo que especificar todos estos campos nuevamente, como:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=2,b=3,c=4,d=5,e=6,f=7,g=8

O:

INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY UPDATE a=VALUES(a),b=VALUES(b),c=VALUES(c),d=VALUES(d),e=VALUES(e),f=VALUES(f),g=VALUES(g)

Ya especifiqué todo en el inserto ...

¡Una nota adicional, me gustaría usar el trabajo para obtener la identificación!

id=LAST_INSERT_ID(id)

Espero que alguien pueda decirme cuál es la forma más eficiente.

Roy
fuente
9
AFAIK, el método de reiniciar cada columna, a=VALUES(a), b=VALUES(b), ...es el camino que debe seguir. Así es como lo hago para todas mis INSERT ON DUPLICATE KEY UPDATEdeclaraciones.
Valdogg21
44
Solo para aclarar, todo lo que se indica aquí es correcto siempre y cuando comprenda que la pregunta que se responde es: ¿Debe repetir cada columna que DESEA ACTUALIZAR? Sí, si desea insertar o actualizar 8 columnas en una tabla de 25 columnas, debe indicar las 8 columnas dos veces, una en la parte de inserción y otra en la parte de actualización. (Puede omitir la clave principal en la parte de actualización, pero es más fácil preformatear una cadena "a = 1, b = 2, c = 3" e incrustarla dos veces). Sin embargo, NO tiene que volver a exponer las 17 columnas restantes No quieres cambiar. Si se convierte en una ACTUALIZACIÓN, mantendrán sus valores existentes.
Timberline
3
Agregué ese comentario porque las preguntas y respuestas me dejaron inseguro sobre las columnas no mencionadas con este tipo de INSERT, que luego probé después de aprender exactamente qué probar. INSERTAR EN LA ACTUALIZACIÓN DE LLAVE DUPLICADA deja las columnas no mencionadas sin cambios en ACTUALIZAR pero les da valores predeterminados en INSERTAR. Con REPLACE INTO, las columnas no mencionadas siempre obtienen valores predeterminados, nunca valores existentes.
Timberline
1
Consulte stackoverflow.com/questions/2472229/… , creo que hay lo que está buscando
Chococroc el

Respuestas:

82

La UPDATEdeclaración se proporciona para que los campos más antiguos se puedan actualizar a un nuevo valor. Si sus valores anteriores son los mismos que los nuevos, ¿por qué necesitaría actualizarlo en cualquier caso?

Por ej. si sus columnas aa gya están establecidos como 2a 8; no habría necesidad de volver a actualizarlo.

Alternativamente, puede usar:

INSERT INTO table (id,a,b,c,d,e,f,g)
VALUES (1,2,3,4,5,6,7,8) 
ON DUPLICATE KEY
    UPDATE a=a, b=b, c=c, d=d, e=e, f=f, g=g;

Para obtener el idde LAST_INSERT_ID; debe especificar la aplicación de back-end que está utilizando para la misma.

Para LuaSQL, a conn:getlastautoid()obtiene el valor.

hjpotter92
fuente
66
Es solo por el ejemplo. En una consulta de la vida real hay diferencias. Mi pregunta es bastante simple: ¿Realmente necesito especificar todos los campos (y valores en mi primer ejemplo) si son los mismos que en la inserción? Solo quiero insertar todo o si hay una coincidencia de valor única: actualizar todo.
Roy
1
"¡Una nota adicional, me gustaría usar el trabajo para obtener la identificación también!"
Agamemnus
1
@Tresdin, por lo que puedo decir, es solo azúcar sintáctico. Personalmente, nunca he visto a nadie que use los valores a = VALUES (a), b = VALUES (b), etc. ¿Quizás pueda asignar varios valores a la columna? Sin embargo, no estoy seguro de por qué no concatenarías los datos de antemano.
DuckPuncher
54
Si la tabla tblya tiene una fila (1,10), entonces: INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=VALUES(a)establecerá a = 2 . Mientras INSERT INTO tbl(id, a) VALUES(1,2) ON DUPLICATE KEY UPDATE a=aestablecerá a = 10
Bùi Việt Thành
2
¿Por qué todos los votos a favor? Esto no funciona. Como señaló @ Bùi Việt Thành, el uso de a=aactualizaciones no es nada. (Las consultas en la pregunta original en realidad tampoco actualizan nada, pero una actualización parece ser lo que pretendía el OP.)
Roger Dueck
41

Hay una extensión específica de MySQL para SQL que puede ser lo que desea: REPLACE INTO

Sin embargo, no funciona exactamente igual que 'ACTUALIZACIÓN DUPLICADA'

  1. Elimina la fila anterior que choca con la nueva fila y luego inserta la nueva fila. Siempre y cuando no tenga una clave principal en la tabla, eso estaría bien, pero si lo tiene, si alguna otra tabla hace referencia a esa clave primaria

  2. No puede hacer referencia a los valores en las filas anteriores, por lo que no puede hacer un equivalente de

    INSERT INTO mytable (id, a, b, c) values ( 1, 2, 3, 4) 
    ON DUPLICATE KEY UPDATE
    id=1, a=2, b=3, c=c + 1;

¡Me gustaría utilizar el trabajo para obtener la identificación!

Eso debería funcionar: last_insert_id () debería tener el valor correcto siempre que su clave principal se incremente automáticamente.

Sin embargo, como dije, si realmente usa esa clave primaria en otras tablas, REPLACE INTO probablemente no sea aceptable para usted, ya que elimina la fila anterior que chocó a través de la clave única.

Alguien más sugirió antes de que pueda reducir algo de escritura haciendo:

INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);
Danack
fuente
Entonces, ¿no puedo seguir con la clave principal antes de una replace intoconsulta?
Roy
No: esa fila se elimina y se crea una nueva fila, que tendrá una nueva clave principal.
Danack
¿Esto no retrasaría el proceso en grandes conjuntos de datos y le gustaría importar csv con 100K o más filas?
Muhammad Omer Aslam
24

No hay otra manera, tengo que especificar todo dos veces. Primero para la inserción, segundo en el caso de actualización.

Roy
fuente
9
Esto es incorrecto y no debe ser la respuesta aceptada. La respuesta de @Danack es la respuesta correcta.
Wayne Workman
2
Debería volver a leer la pregunta @WayneWorkman, esta es la respuesta correcta.
Roy
24

Aquí hay una solución a su problema:

He tratado de resolver problemas como el tuyo y quiero sugerir que pruebes desde un aspecto simple.

Siga estos pasos: Aprenda de una solución simple.

Paso 1: cree un esquema de tabla con esta consulta SQL:

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) NOT NULL,
  `password` varchar(32) NOT NULL,
  `status` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `no_duplicate` (`username`,`password`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

Una tabla <code> usuario </code> con datos

Paso 2: cree un índice de dos columnas para evitar la duplicación de datos utilizando la siguiente consulta SQL:

ALTER TABLE `user` ADD INDEX no_duplicate (`username`, `password`);

o Cree un índice de dos columnas desde la GUI de la siguiente manera: Crear índice desde la GUI Seleccionar columnas para crear índice Índices de la tabla <code> usuario </code>

Paso 3: Actualice si existe, inserte si no utiliza las siguientes consultas:

INSERT INTO `user`(`username`, `password`) VALUES ('ersks','Nepal') ON DUPLICATE KEY UPDATE `username`='master',`password`='Nepal';

INSERT INTO `user`(`username`, `password`) VALUES ('master','Nepal') ON DUPLICATE KEY UPDATE `username`='ersks',`password`='Nepal';

Tabla <code> usuario </code> después de ejecutar la consulta anterior


fuente
1
Gracias por este tutorial, me hizo comprender, muy apreciado, ¡gracias!
Michael Nilsson
Para su información, almacenar contraseñas en texto plano es una idea terrible, ya que está tratando de evitar contraseñas duplicadas a nivel de base de datos.
OrangeDog
10

En caso de que pueda utilizar un lenguaje de secuencias de comandos para preparar sus consultas SQL, puede reutilizar pares de campo = valor utilizando SET lugar de (a,b,c) VALUES(a,b,c).

Un ejemplo con PHP:

$pairs = "a=$a,b=$b,c=$c";
$query = "INSERT INTO $table SET $pairs ON DUPLICATE KEY UPDATE $pairs";

Tabla de ejemplo:

CREATE TABLE IF NOT EXISTS `tester` (
  `a` int(11) NOT NULL,
  `b` varchar(50) NOT NULL,
  `c` text NOT NULL,
  UNIQUE KEY `a` (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
Chris
fuente
44
Debería escapar de sus valores o ejecutar una declaración preparada con sus valores.
Chinoto Vokro
1
@ChinotoVokro - Estoy de acuerdo. Esto solo presenta un "cómo pudiste" y ni siquiera está usando ninguna función de consulta en el ejemplo.
Chris
1
Esta es una buena alternativa para especificar una matriz de claves y una matriz de valores. Gracias.
Mark Carpenter Jr
5

Es posible que desee considerar el uso de la REPLACE INTOsintaxis, pero tenga en cuenta que, al duplicar la tecla PRIMARIO / ÚNICO, BORRARÁ la fila e INSERTA una nueva.

No necesitará volver a especificar todos los campos. Sin embargo, debe considerar la posible reducción del rendimiento (depende del diseño de su tabla).

Advertencias:

  • Si tiene la clave primaria AUTO_INCREMENT, se le dará una nueva
  • Probablemente sea necesario actualizar los índices
Matej
fuente
hay una violación de restricción si el índice también es una clave foránea para otra tabla, se activa al eliminar la parte.
user3290180
4

Sé que es tarde, pero espero que alguien reciba ayuda de esta respuesta

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

Puedes leer el tutorial a continuación aquí:

https://mariadb.com/kb/en/library/insert-on-duplicate-key-update/

http://www.mysqltutorial.org/mysql-insert-or-update-on-duplicate-key-update/

Galang Piliang
fuente
Buena respuesta. Pero el uso de VALUES () para referirse a las nuevas filas está en desuso a partir de MySQL 8.0.20 . El nuevo enfoque, detallado en el enlace, es utilizar un alias para la fila. En este ejemplo, el alias es new:INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new ON DUPLICATE KEY UPDATE c = new.a+new.b;
user697473
@ user697473 Recibo el error: "Tiene un error en su sintaxis SQL; consulte el manual que corresponde a la versión de su servidor MariaDB para obtener la sintaxis correcta para usar cerca de 'COMO NUEVO EN LA ACTUALIZACIÓN DE LA CLAVE DUPLICADA a = new.a'" , y mi consulta estaba funcionando (excepto cuando se produjeron claves duplicadas, obviamente) antes de implementar esta cláusula. ¿Algunas ideas?
Aaron
@aaron - lo siento, no hay grandes ideas. MySQL 8.0.20 se acaba de lanzar a finales de abril; Quizás MariaDB no se ha puesto al día todavía. Eso podría explicar por qué te está dando un error.
user697473
@ user697473 Sí, en realidad acabo de probar el método a = VALUES (a) en la respuesta original y funcionó, de todos modos, gracias.
Aaron
-7

puede usar insert ignore para tal caso, ignorará si obtiene registros duplicados INSERT IGNORE ...; - sin EN LLAVE DUPLICADA

pitu
fuente
Quería insertarlo cuando no está presente, de lo contrario, actualizarlo. No lo ignores.
Roy
por qué desea actualizarlo si está actualizando con el valor anterior / mismo, si es un valor nuevo, entonces está bien con él, si no lo inserta, ignore el trabajo para él
pitu
2
Lo más probable es que los valores puedan cambiar, y quiere crear un registro si no existe, pero actualizar un registro existente si lo hace (con cambios) sin la sobrecarga de un viaje de ida y vuelta a la aplicación y luego a la base de datos de nuevo.
mopsyd