Todo lo siguiente se aplica a InnoDB.
Siento que conocer las velocidades de los 3 métodos diferentes es importante.
Hay 3 métodos:
- INSERT: INSERT con ON DUPLICATE KEY UPDATE
- TRANSACCIÓN: Cuando se realiza una actualización para cada registro dentro de una transacción
- CASO: En la que un caso/cuando para cada registro diferente dentro de un UPDATE
Acabo de probar esto, y el método de inserción se 6.7x más rápido para mí que el TRAN Método de SACCIÓN Probé en un conjunto de 3.000 y 30.000 filas.
El método TRANSACTION aún tiene que ejecutar cada consulta individualmente, lo que lleva tiempo, aunque carga los resultados en la memoria, o algo así, durante la ejecución. El método TRANSACTION también es bastante caro en registros de replicación y de consulta.
Peor aún, el método CASE era 41.1x más lento que el método INSERT con 30,000 registros (6.1x más lento que TRANSACTION). Y 75x más lento en MyISAM. Los métodos INSERT y CASE se rompieron incluso en ~ 1,000 registros. Incluso en 100 registros, el método CASE es MUY más rápido.
Por lo tanto, en general, creo que el método INSERT es el mejor y el más fácil de usar. Las consultas son más pequeñas y fáciles de leer y solo ocupan 1 consulta de acción. Esto se aplica tanto a InnoDB como a MyISAM.
Bono cosas:
La solución para el problema del campo no predeterminado INSERT es desactivar temporalmente los modos SQL relevantes: SET SESSION sql_mode=REPLACE(REPLACE(@@SESSION.sql_mode,"STRICT_TRANS_TABLES",""),"STRICT_ALL_TABLES","")
. Asegúrese de guardar el sql_mode
primero si planea revertirlo.
En cuanto a otros comentarios que he visto que dicen que el auto_incremento se incrementa con el método INSERT, 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 php intérprete sobrecarga
<?
//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).';');
}
Si no hay duplicados, entonces no quiero que se inserte esa fila. ¿Qué debería hacer? porque estoy obteniendo información de otro sitio que mantiene tablas con id. Estoy insertando valores con respecto a esa identificación. si el sitio tiene nuevos registros, terminaré insertando solo los identificadores y el recuento, excepto toda la demás información. si y solo si hay una entrada para el ID, entonces debería actualizarse, de lo contrario debería omitirse. ¿Qué debo hacer? –
así que si es como insertar en salto nulo u omitir vacío en la actualización duplicada ... entonces eso sería bueno. –
Nota: esta respuesta también asume que la ID es la clave principal – JM4