2011-05-09 55 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