Pregunta

Sé que puedes insertar varias filas a la vez, ¿hay alguna manera de actualizar varias filas a la vez (como en una consulta) en MySQL?

Editar:Por ejemplo tengo lo siguiente

Name   id  Col1  Col2
Row1   1    6     1
Row2   2    2     3
Row3   3    9     5
Row4   4    16    8

Quiero combinar todas las siguientes actualizaciones en una sola consulta.

UPDATE table SET Col1 = 1 WHERE id = 1;
UPDATE table SET Col1 = 2 WHERE id = 2;
UPDATE table SET Col2 = 3 WHERE id = 3;
UPDATE table SET Col1 = 10 WHERE id = 4;
UPDATE table SET Col2 = 12 WHERE id = 4;
¿Fue útil?

Solución

Sí, eso es posible: puedes usar INSERT...EN ACTUALIZACIÓN DE LLAVE DUPLICADA.

Usando tu ejemplo:

INSERT INTO table (id,Col1,Col2) VALUES (1,1,1),(2,2,3),(3,9,3),(4,10,12)
ON DUPLICATE KEY UPDATE Col1=VALUES(Col1),Col2=VALUES(Col2);

Otros consejos

Como tiene valores dinámicos, necesita usar IF o CASE para que las columnas se actualicen.Se pone un poco feo, pero debería funcionar.

Usando tu ejemplo, podrías hacerlo así:

UPDATE table SET Col1 = CASE id 
                          WHEN 1 THEN 1 
                          WHEN 2 THEN 2 
                          WHEN 4 THEN 10 
                          ELSE Col1 
                        END, 
                 Col2 = CASE id 
                          WHEN 3 THEN 3 
                          WHEN 4 THEN 12 
                          ELSE Col2 
                        END
             WHERE id IN (1, 2, 3, 4);

La pregunta es antigua, pero me gustaría ampliar el tema con otra respuesta.

Lo que quiero decir es que la forma más sencilla de lograrlo es simplemente agrupar varias consultas con una transacción.La respuesta aceptada INSERT ... ON DUPLICATE KEY UPDATE Es un buen truco, pero hay que tener en cuenta sus inconvenientes y limitaciones:

  • Como ya se ha dicho, si inicia la consulta con filas cuyas claves principales no existen en la tabla, la consulta inserta nuevos registros "a medias".Probablemente no sea lo que quieres
  • Si tiene una tabla con un campo no nulo sin valor predeterminado y no desea tocar este campo en la consulta, obtendrá "Field 'fieldname' doesn't have a default value" Advertencia de MySQL incluso si no inserta una sola fila.Te meterá en problemas si decides ser estricto y convertir las advertencias de MySQL en excepciones de tiempo de ejecución en tu aplicación.

Hice algunas pruebas de rendimiento para tres de las variantes sugeridas, incluida la INSERT ... ON DUPLICATE KEY UPDATE variante, una variante con cláusula "caso/cuándo/entonces" y un enfoque ingenuo con la transacción.Puede obtener el código Python y los resultados. aquí.La conclusión general es que la variante con declaración de caso resulta ser dos veces más rápida que otras dos variantes, pero es bastante difícil escribir código correcto y seguro para la inyección, por lo que personalmente me quedo con el enfoque más simple:utilizando transacciones.

Editar: Hallazgos de dakusan demostrar que mis estimaciones de rendimiento no son del todo válidas.Por favor mira esta respuesta para otra investigación más elaborada.

No estoy seguro de por qué aún no se menciona otra opción útil:

UPDATE my_table m
JOIN (
    SELECT 1 as id, 10 as _col1, 20 as _col2
    UNION ALL
    SELECT 2, 5, 10
    UNION ALL
    SELECT 3, 15, 30
) vals ON m.id = vals.id
SET col1 = _col1, col2 = _col2;

Todo lo siguiente se aplica a InnoDB.

Siento que es importante conocer las velocidades de los 3 métodos diferentes.

Hay 3 métodos:

  1. INSERTAR:INSERTAR con ACTUALIZACIÓN DE LLAVE DUPLICADA
  2. TRANSACCIÓN:Donde haces una actualización para cada registro dentro de una transacción
  3. CASO:¿En qué caso/cuándo para cada registro diferente dentro de una ACTUALIZACIÓN?

Acabo de probar esto y el método INSERT fue 6,7x más rápido para mí que el método TRANSACTION.Probé un conjunto de 3.000 y 30.000 filas.

El método TRANSACTION todavía tiene que ejecutar cada consulta individualmente, lo que lleva tiempo, aunque agrupa los resultados en la memoria, o algo así, durante la ejecución.El método TRANSACTION también es bastante caro tanto en los registros de replicación como de consultas.

Peor aún, el método CASE fue 41,1x más lento que el método INSERT con 30.000 registros (6,1 veces más lento que TRANSACTION).Y 75x más lento en MyISAM.Los métodos INSERT y CASE alcanzaron un punto de equilibrio con ~1000 registros.Incluso con 100 registros, el método CASE es APENAS más rápido.

Entonces, en general, creo que el método INSERT es mejor y más fácil de usar.Las consultas son más pequeñas y más fáciles de leer y solo requieren 1 consulta de acción.Esto se aplica tanto a InnoDB como a MyISAM.

Cosas extra:

La solución para el problema INSERT de campo no predeterminado es desactivar temporalmente los modos SQL relevantes: SET SESSION sql_mode=REPLACE(REPLACE(@@SESSION.sql_mode,"STRICT_TRANS_TA‌​BLES",""),"STRICT_AL‌​L_TABLES","").Asegúrate de guardar el sql_mode primero si planeas revertirlo.

En cuanto a otros comentarios que he visto que dicen que auto_increment aumenta usando el método INSERT, también lo probé y parece que no es el caso.

El código para ejecutar las pruebas es el siguiente.También genera archivos .SQL para eliminar la sobrecarga del intérprete de PHP.

<?
//Variables
$NumRows=30000;

//These 2 functions need to be filled in
function InitSQL()
{

}
function RunSQLQuery($Q)
{

}

//Run the 3 tests
InitSQL();
for($i=0;$i<3;$i++)
    RunTest($i, $NumRows);

function RunTest($TestNum, $NumRows)
{
    $TheQueries=Array();
    $DoQuery=function($Query) use (&$TheQueries)
    {
        RunSQLQuery($Query);
        $TheQueries[]=$Query;
    };

    $TableName='Test';
    $DoQuery('DROP TABLE IF EXISTS '.$TableName);
    $DoQuery('CREATE TABLE '.$TableName.' (i1 int NOT NULL AUTO_INCREMENT, i2 int NOT NULL, primary key (i1)) ENGINE=InnoDB');
    $DoQuery('INSERT INTO '.$TableName.' (i2) VALUES ('.implode('), (', range(2, $NumRows+1)).')');

    if($TestNum==0)
    {
        $TestName='Transaction';
        $Start=microtime(true);
        $DoQuery('START TRANSACTION');
        for($i=1;$i<=$NumRows;$i++)
            $DoQuery('UPDATE '.$TableName.' SET i2='.(($i+5)*1000).' WHERE i1='.$i);
        $DoQuery('COMMIT');
    }

    if($TestNum==1)
    {
        $TestName='Insert';
        $Query=Array();
        for($i=1;$i<=$NumRows;$i++)
            $Query[]=sprintf("(%d,%d)", $i, (($i+5)*1000));
        $Start=microtime(true);
        $DoQuery('INSERT INTO '.$TableName.' VALUES '.implode(', ', $Query).' ON DUPLICATE KEY UPDATE i2=VALUES(i2)');
    }

    if($TestNum==2)
    {
        $TestName='Case';
        $Query=Array();
        for($i=1;$i<=$NumRows;$i++)
            $Query[]=sprintf('WHEN %d THEN %d', $i, (($i+5)*1000));
        $Start=microtime(true);
        $DoQuery("UPDATE $TableName SET i2=CASE i1\n".implode("\n", $Query)."\nEND\nWHERE i1 IN (".implode(',', range(1, $NumRows)).')');
    }

    print "$TestName: ".(microtime(true)-$Start)."<br>\n";

    file_put_contents("./$TestName.sql", implode(";\n", $TheQueries).';');
}
UPDATE table1, table2 SET table1.col1='value', table2.col1='value' WHERE table1.col3='567' AND table2.col6='567'

Esto debería funcionar para ti.

Hay una referencia en el manual de mysql para varias tablas.

Utilice una tabla temporal

// Reorder items
function update_items_tempdb(&$items)
{
    shuffle($items);
    $table_name = uniqid('tmp_test_');
    $sql = "CREATE TEMPORARY TABLE `$table_name` ("
        ."  `id` int(10) unsigned NOT NULL AUTO_INCREMENT"
        .", `position` int(10) unsigned NOT NULL"
        .", PRIMARY KEY (`id`)"
        .") ENGINE = MEMORY";
    query($sql);
    $i = 0;
    $sql = '';
    foreach ($items as &$item)
    {
        $item->position = $i++;
        $sql .= ($sql ? ', ' : '')."({$item->id}, {$item->position})";
    }
    if ($sql)
    {
        query("INSERT INTO `$table_name` (id, position) VALUES $sql");
        $sql = "UPDATE `test`, `$table_name` SET `test`.position = `$table_name`.position"
            ." WHERE `$table_name`.id = `test`.id";
        query($sql);
    }
    query("DROP TABLE `$table_name`");
}

Puede asignar un alias a la misma tabla para obtener las identificaciones que desea insertar (si está realizando una actualización fila por fila:

UPDATE table1 tab1, table1 tab2 -- alias references the same table
SET 
col1 = 1
,col2 = 2
. . . 
WHERE 
tab1.id = tab2.id;

Además, debería parecer obvio que también puedes actualizar desde otras tablas.En este caso, la actualización funciona como una declaración "SELECT", que le proporciona los datos de la tabla que está especificando.Usted está indicando explícitamente en su consulta los valores de actualización, por lo que la segunda tabla no se ve afectada.

¿Por qué nadie menciona múltiples declaraciones en una consulta?

En php, usas multi_query método de instancia de mysqli.

Desde el manual de php

MySQL opcionalmente permite tener múltiples declaraciones en una cadena de declaración.El envío de varios extractos a la vez reduce los viajes de ida y vuelta entre el cliente y el servidor, pero requiere un manejo especial.

Aquí está el resultado en comparación con otros 3 métodos en la actualización 30,000 sin procesar.El código se puede encontrar. aquí que se basa en la respuesta de @Dakusan

Transacción:5.5194580554962
Insertar:0.20669293403625
Caso:16.474853992462
Multi:0.0412278175354

Como puede ver, la consulta de varias declaraciones es más eficiente que la respuesta más alta.

Si recibe un mensaje de error como este:

PHP Warning:  Error while sending SET_OPTION packet

Es posible que necesite aumentar el max_allowed_packet en el archivo de configuración MySQL que en mi máquina es /etc/mysql/my.cnf y luego reinicie mysqld.

Quizás también le interese utilizar uniones en las actualizaciones, lo cual también es posible.

Update someTable Set someValue = 4 From someTable s Inner Join anotherTable a on s.id = a.id Where a.id = 4
-- Only updates someValue in someTable who has a foreign key on anotherTable with a value of 4.

Editar:Si los valores que está actualizando no provienen de ningún otro lugar de la base de datos, deberá realizar varias consultas de actualización.

Hay una configuración que puede modificar llamada 'declaración múltiple' que deshabilita el 'mecanismo de seguridad' de MySQL implementado para evitar (más de un) comando de inyección.Típico de la "brillante" implementación de MySQL, también impide que el usuario realice consultas eficientes.

Aquí (http://dev.mysql.com/doc/refman/5.1/en/mysql-set-server-option.html) hay información sobre la implementación C de la configuración.

Si está usando PHP, puede usar mysqli para realizar declaraciones múltiples (creo que php se incluye con mysqli desde hace un tiempo)

$con = new mysqli('localhost','user1','password','my_database');
$query = "Update MyTable SET col1='some value' WHERE id=1 LIMIT 1;";
$query .= "UPDATE MyTable SET col1='other value' WHERE id=2 LIMIT 1;";
//etc
$con->multi_query($query);
$con->close();

Espero que ayude.

usar

REPLACE INTO`table` VALUES (`id`,`col1`,`col2`) VALUES
(1,6,1),(2,2,3),(3,9,5),(4,16,8);

Tenga en cuenta:

  • La identificación tiene que ser una clave única primaria.
  • Si usa claves extranjeras para hacer referencia a la tabla, reemplace las eliminaciones y luego se inserta, por lo que esto podría causar un error

Lo siguiente actualizará todas las filas en una tabla.

Update Table Set
Column1 = 'New Value'

El siguiente actualizará todas las filas donde el valor de la Columna2 sea superior a 5

Update Table Set
Column1 = 'New Value'
Where
Column2 > 5

hay de todo Tecnología desconocidaEjemplo de actualización de más de una tabla.

UPDATE table1, table2 SET
table1.col1 = 'value',
table2.col1 = 'value'
WHERE
table1.col3 = '567'
AND table2.col6='567'

Sí ... es posible usar la instrucción SQL INSERT ON DUPLICATE KEY UPDATE ...sintaxis:Insertar en valores de table_name (a, b, c) (1,2,3), (4,5,6) en la actualización de clave duplicada a = valores (a), b = valores (b), c = valores (c)

Con PHP hice esto.Utilice punto y coma, divídalo en una matriz y luego envíelo mediante un bucle.

$con = new mysqli('localhost','user1','password','my_database');
$batchUpdate = true; /*You can choose between batch and single query */
$queryIn_arr = explode(";", $queryIn);

if($batchUpdate)    /* My SQL prevents multiple insert*/
{
    foreach($queryIn_arr as $qr)
    {
        if(strlen($qr)>3)
        {
            //echo '<br>Sending data to SQL1:<br>'.$qr.'</br>';
            $result = $conn->query($qr);
        }

    }
}
else
{
    $result = $conn->query($queryIn);
}
$con->close();
UPDATE tableName SET col1='000' WHERE id='3' OR id='5'

Esto debería lograr lo que estás buscando.Simplemente agregue más identificaciones.Lo he probado.

UPDATE `your_table` SET 

`something` = IF(`id`="1","new_value1",`something`), `smth2` = IF(`id`="1", "nv1",`smth2`),
`something` = IF(`id`="2","new_value2",`something`), `smth2` = IF(`id`="2", "nv2",`smth2`),
`something` = IF(`id`="4","new_value3",`something`), `smth2` = IF(`id`="4", "nv3",`smth2`),
`something` = IF(`id`="6","new_value4",`something`), `smth2` = IF(`id`="6", "nv4",`smth2`),
`something` = IF(`id`="3","new_value5",`something`), `smth2` = IF(`id`="3", "nv5",`smth2`),
`something` = IF(`id`="5","new_value6",`something`), `smth2` = IF(`id`="5", "nv6",`smth2`) 

// Simplemente lo construyes en php como

$q = 'UPDATE `your_table` SET ';

foreach($data as $dat){

  $q .= '

       `something` = IF(`id`="'.$dat->id.'","'.$dat->value.'",`something`), 
       `smth2` = IF(`id`="'.$dat->id.'", "'.$dat->value2.'",`smth2`),';

}

$q = substr($q,0,-1);

Para que pueda actualizar la tabla de agujeros con una consulta

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top