2019年编辑
7 年后 Sammitch 在这里说我最初的答案很糟糕。我什至无法弄清楚我在说什么“fgetcsv()
资源使用问题”。7 年前的 PHP 可能缺少今天的一些 IO 流优化,但我愿意认为这是与 PHP 无关的资源限制。
Jay Dhameliya 在下面的回答很可能是您想要的方式。 LOAD DATA INFILE
应该尽可能快地将数据直接发送到 mySQL 中。
为了完整起见,假设有一些东西阻止使用LOAD DATA INFILE
[比如最近发现的巨大安全漏洞],并且您希望有效地从文件加载数据,您可能希望利用事务来批量 IO 和索引写入。例如:
$fname = 'myfile.csv';
if( ! $fh = fopen($myfile, 'r') ) {
throw new Exception("Could not open $fname for reading.");
}
$dbh = new PDO(...);
$dbh->beginTransaction();
$stmt = $dbh->prepare('INSERT INTO table VALUES (?,?,?,...)')
try {
while( $params = fgetcsv($fh) ) {
$stmt->execute($params);
}
} catch( \PDOException $e ) {
$dbh->rollBack();
throw $e;
}
$dbh->commit();
将所有内容批处理到单个事务中仍然LOAD DATA INFILE
是速度如此之快的部分原因,并且很可能是@Benjamin 建议使用扩展插入的很大一部分。
原始答案
- LOAD DATA LOCAL INFILE 在... PHP 中被禁止
- 确保 mySQL 和 www 用户都可以访问相关文件。
或者:fgetcsv()
以编程方式使用和创建插入。
编辑:
为了避免 [因为它尝试一次读取整个文件] 的资源使用问题,fgetcsv()
您可以创建一个类似于下面的循环来读取/插入可管理的块。
<?php
$fname = 'myfile.csv';
$chunksize = 50;
if( ! $fh = fopen($myfile, 'r') ) {
throw new Exception("Could not open $fname for reading.");
}
$i=0;
$buffer = array()
while(!feof($fh)) {
$buffer[] = fgets($fh);
$i++;
if( ($i % $chunksize) == 0 ) {
commit_buffer($buffer);
//run inserts
$buffer = array(); //blank out the buffer.
}
}
//clean out remaining buffer entries.
if( count($buffer) ) { commit_buffer($buffer); }
function commit_buffer($buffer) {
foreach( $buffer as $line ) {
$fields = explode(',', $line);
//create inserts
}
//run inserts
$buffer = array(); //blank out the buffer.
}
这样$chunksize
,在任何给定时间,只有行保存在内存中。
您可能需要额外的代码来处理诸如包含逗号和换行符的封装字符串之类的事情,但是如果您无法开始LOAD DATA LOCAL INFILE
工作,我看不到太多其他选择。