以下所有内容都适用于 InnoDB。
我觉得了解三种不同方法的速度很重要。
有3种方法:
- 插入:插入重复键更新
- TRANSACTION:您在其中对事务中的每条记录进行更新
- 案例:对于 UPDATE 中的每个不同记录,您的案例/时间
我刚刚对此进行了测试,对我来说,INSERT 方法比 TRANSACTION 方法快6.7 倍。我尝试了一组 3,000 和 30,000 行。
TRANSACTION 方法仍然必须运行每个单独的查询,这需要时间,尽管它在执行时将结果批处理到内存或其他东西中。TRANSACTION 方法在复制和查询日志中也非常昂贵。
更糟糕的是,CASE 方法比具有 30,000 条记录的 INSERT 方法慢 41.1 倍(比TRANSACTION慢 6.1 倍)。在MyISAM中慢了 75 倍。INSERT 和 CASE 方法在大约 1,000 条记录时实现了收支平衡。即使有 100 条记录,CASE 方法也几乎没有更快的速度。
所以总的来说,我觉得 INSERT 方法是最好的,也是最容易使用的。查询更小更易于阅读,并且只占用 1 个操作查询。这适用于 InnoDB 和 MyISAM。
奖励的东西:
INSERT非默认字段问题的解决方法是暂时关闭相关的SQL模式:SET SESSION sql_mode=REPLACE(REPLACE(@@SESSION.sql_mode,"STRICT_TRANS_TABLES",""),"STRICT_ALL_TABLES","")
. sql_mode
如果您打算恢复它,请确保保存第一个。
至于我看到的其他评论说 auto_increment 使用 INSERT 方法上升,这在 InnoDB 中似乎是这种情况,但不是 MyISAM。
运行测试的代码如下。它还输出 .SQL 文件以消除 php 解释器开销
<?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).';');
}