Tất cả những điều sau đây áp dụng cho InnoDB.
Tôi cảm thấy biết tốc độ của 3 phương pháp khác nhau là quan trọng.
Có 3 phương pháp:
- XÁC NHẬN: XÁC NHẬN VỚI CẬP NHẬT TỪ KHÓA
- GIAO DỊCH: Nơi bạn thực hiện cập nhật cho mỗi bản ghi trong một giao dịch
- TRƯỜNG HỢP: Trong đó bạn là một trường hợp / khi cho mỗi bản ghi khác nhau trong một CẬP NHẬT
Tôi mới thử nghiệm điều này và phương pháp INSERT là 6,7 lần nhanh hơn lần so với phương thức GIAO DỊCH. Tôi đã thử trên một bộ gồm cả 3.000 và 30.000 hàng.
Phương thức TRANSACTION vẫn phải chạy từng truy vấn riêng lẻ, điều này sẽ mất thời gian, mặc dù nó bó các kết quả trong bộ nhớ hoặc một cái gì đó trong khi thực hiện. Phương thức TRANSACTION cũng khá tốn kém trong cả bản ghi truy vấn và sao chép.
Tệ hơn nữa, phương pháp CASE là 41,1 lần chậm hơn so với phương pháp INSERT w / 30.000 bản ghi (chậm hơn 6,1 lần so với GIAO DỊCH). Và chậm hơn 75 lần trong MyISAM. Phương pháp INSERT và CASE đã phá vỡ thậm chí ở mức ~ 1.000 hồ sơ. Ngay cả ở 100 bản ghi, phương pháp CASE vẫn nhanh hơn.
Vì vậy, nói chung, tôi cảm thấy phương pháp INSERT là tốt nhất và dễ sử dụng nhất. Các truy vấn nhỏ hơn và dễ đọc hơn và chỉ chiếm 1 truy vấn hành động. Điều này áp dụng cho cả InnoDB và MyISAM.
Phần thưởng:
Giải pháp cho vấn đề không phải trường mặc định của INSERT là tạm thời tắt các chế độ SQL có liên quan : SET SESSION sql_mode=REPLACE(REPLACE(@@SESSION.sql_mode,"STRICT_TRANS_TABLES",""),"STRICT_ALL_TABLES","")
. Hãy chắc chắn để lưu sql_mode
đầu tiên nếu bạn có kế hoạch hoàn nguyên nó.
Đối với các bình luận khác mà tôi đã thấy nói rằng auto_increment tăng lên khi sử dụng phương thức INSERT, điều này dường như là trường hợp trong InnoDB, nhưng không phải là MyISAM.
Mã để chạy các bài kiểm tra như sau. Nó cũng xuất ra các tệp .Query để loại bỏ trình thông dịch 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).';');
}