2011-05-09 99 views
1

我有大約14000行的逗號分隔值的,我想插入使用PHP PDO,像這樣一個SQLite表:的SQLite大量的插入使用PHP

<?php 
// create a PDO object 
$dbh = new PDO('sqlite:mydb.sdb'); 

$lines = file('/csv/file.txt'); // import lines as array 
foreach ($lines as $line) { 
    $line_array = (','$line); // create an array of comma-separated values in each line 
    $values = ''; 
    foreach ($line_array as $l) { 
     $values .= "'$l', "; 
    } 
    substr($values,-2,0); // get rid of the last comma and whitespace 
    $query = "insert into sqlite_table values ($values)"; // plug the value into a query statement 
    $dbh->query($query); // run the query 
} 

?> 

這個查詢需要很長的時間,並不中斷地運行它,我將不得不使用PHP-CLI。 有沒有更好的(更快)的方法來做到這一點?

回答

16

通過將插入包裝在單個事務中,您將看到很好的性能提升。如果你不這樣做SQLite將每個插入作爲它自己的事務處理。

<?php 
// create a PDO object 
$dbh = new PDO('sqlite:mydb.sdb'); 

// Start transaction 
$dbh->beginTransaction(); 
$lines = file('/csv/file.txt'); // import lines as array 
foreach ($lines as $line) { 
    $line_array = (','$line); // create an array of comma-separated values in each line 
    $values = ''; 
    foreach ($line_array as $l) { 
     $values .= "'$l', "; 
    } 
    substr($values,-2,0); // get rid of the last comma and whitespace 
    $query = "insert into sqlite_table values ($values)"; // plug the value into a query statement 
    $dbh->query($query); // run the query 
} 
// commit transaction 
$dbh->commit(); 

?> 
+0

這對我來說非常合適。我驚訝於它導入一切的速度有多快。謝謝。 – topmulch 2011-05-10 02:13:59

+0

謝謝,你爲我節省了很多時間! :) – Czechnology 2013-08-19 08:43:15

+0

爲了獲得更多的速度,請使用與交易結合的準備工作。在可能影響實際查詢的值的情況下,也會使事情更安全,例如引發意外的引號。 – 2015-06-23 10:31:40

1

SQLlite FAQ

交易速度由硬盤驅動器速度的限制,因爲(默認) SQLite的實際等待,直到數據 真的被安全地存儲在磁盤 表面上之前的交易 完成。這樣,如果您突然 失去能力或者如果您的操作系統崩潰,您的數據仍然是安全的。有關詳細信息,請參閱 關於SQLite中的原子提交.. [...]

另一個選項是運行PRAGMA synchronous = OFF。這個命令將會使得SQLite不會等待數據到達 到達磁盤表面,這會使得 寫入操作看起來是 要快得多。但是,如果您在 中失去了交易中的權力,那麼您的 數據庫文件可能會損壞。

我會說這最後一段是你需要的。

編輯:對此沒有把握,但我相信使用sqlite_unbuffered_query()應該可以做到。

2

循環之前開始事務和循環
現在您的代碼工作的方式提交之後,它開始插入

1

在每一個交易如果你正在尋找一個更位速度,使用prepare/fetch,所以SQL引擎不必每次都解析出文本字符串。

$name = $age = ''; 
$insert_stmt = $db->prepare("insert into table (name, age) values (:name, :age)"); 
$insert_stmt->bindValue(':name', $name); 
$insert_stmt->bindValue(':age', $age); 

// do your loop here, like fgetcsv 
while (get the data) { 
list($name, $age) = split(',', $string); 
$insert_stmt->execute(); 
} 

這是反直覺的,你的外循環結合,但是這是一個原因,這種方法是如此之快,你基本上是說:「執行使用這些變量數據這個預編譯的查詢」 。所以它甚至不需要在內部移動數據。並且您希望避免重新解析查詢,如果您使用「insert into table(name)values('$ name')」這樣的問題,那麼每個查詢都會將整個文本字符串發送到數據庫,解析。

還有一點要加快步伐 - 包住整個循環的交易,那麼當循環結束後提交事務。

+0

是否有一些條件可以使此工作?它似乎沒有結束http://stackoverflow.com/questions/34878681/why-does-this-sqlite-transaction-with-prepared-statements-not-work – Thilo 2016-01-19 14:14:35

+1

嗯,是的,它很快,但它不會做你想做的事。你需要在循環中進行綁定,你的代碼會一遍又一遍地插入相同的值。 – Eborbob 2016-02-09 21:48:31