Todos os itens a seguir se aplicam ao InnoDB.
Sinto que é importante conhecer a velocidade dos três métodos diferentes.
Existem 3 métodos:
- INSERT: INSERT com ON DUPLICATE KEY UPDATE
- TRANSACTION: onde você faz uma atualização para cada registro dentro de uma transação
- CASO: No qual você solicita / quando, para cada registro diferente dentro de um UPDATE
Acabei de testar isso, e o método INSERT era 6,7x mais rápido do que o método TRANSACTION. Eu tentei em um conjunto de 3.000 e 30.000 linhas.
O método TRANSACTION ainda precisa executar cada consulta individualmente, o que leva tempo, apesar de agrupar os resultados na memória, ou algo assim, durante a execução. O método TRANSACTION também é bastante caro nos logs de replicação e consulta.
Pior ainda, o método CASE foi 41,1x mais lento que o INSERT com 30.000 registros (6,1x mais lento que TRANSACTION). E 75x mais lento no MyISAM. Os métodos INSERT e CASE chegaram a ~ 1.000 registros. Mesmo com 100 registros, o método CASE é MUITO MAIS rápido.
Portanto, em geral, acho que o método INSERT é melhor e mais fácil de usar. As consultas são menores e mais fáceis de ler e ocupam apenas 1 consulta de ação. Isso se aplica ao InnoDB e ao MyISAM.
Material bônus:
A solução para o problema da não-default-campo INSERT está para desligar temporariamente os modos SQL relevantes: SET SESSION sql_mode=REPLACE(REPLACE(@@SESSION.sql_mode,"STRICT_TRANS_TABLES",""),"STRICT_ALL_TABLES","")
. Salve o sql_mode
primeiro se planeja revertê-lo.
Como em outros comentários que eu vi, dizem que o auto_increment sobe usando o método INSERT, esse parece ser o caso no InnoDB, mas não no MyISAM.
O código para executar os testes é o seguinte. Também gera arquivos .SQL para remover a sobrecarga do interpretador 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).';');
}