Php 在csv上使用文件指针
我想知道如何将下面的代码更改为只读取x行数,然后处理sql insert语句,然后继续按x行数读取文件,并一直处理到文件结束。我对文件指针的概念是陌生的,但我理解使用FGET应该是可能的 我希望将下面的代码改成一个函数,在这个函数中,我可以传递文件名和需要读取和处理的行数 我目前有: (来自) 我希望将内存占用保持在最低限度。我认为这应该只是跟踪指针->重复直到到达行->处理sql->从指针开始->重复直到eofPhp 在csv上使用文件指针,php,mysql,csv,file-pointer,Php,Mysql,Csv,File Pointer,我想知道如何将下面的代码更改为只读取x行数,然后处理sql insert语句,然后继续按x行数读取文件,并一直处理到文件结束。我对文件指针的概念是陌生的,但我理解使用FGET应该是可能的 我希望将下面的代码改成一个函数,在这个函数中,我可以传递文件名和需要读取和处理的行数 我目前有: (来自) 我希望将内存占用保持在最低限度。我认为这应该只是跟踪指针->重复直到到达行->处理sql->从指针开始->重复直到eof fgets() 我是否需要执行回调或类似操作来延迟sql处理,直到读取所有行 我有
fgets()
date_default_timezone_set('Australia/Brisbane');
$date = date('m/d/Y h:i:s a', time());
$timezone = date_default_timezone_get();
$time_start = microtime(true);
$batch_size = 500; // Lines to be read per batch
$batch = 0;
$counter = 0;
$lines = 0;
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Remove Existing Data from table
$sql = "TRUNCATE TABLE `workorderstest`";
$conn->query($sql);
$handle = fopen(dirname(__FILE__)."/files/workorders.csv" , "r");
//instead of executing query one by one,
//let us prepare 1 SQL query that will insert all values from the batch
$sql_prefix ="INSERT INTO workorderstest(id,parentid,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10) VALUES ";
$values = "";
while (($line = fgets($handle)) !== false) {
$values .= "($line),";
$counter++;
$lines++;
if ($counter == $batch_size) {
$values = substr($values, 0, strlen($values) - 1);
$conn->query($sql_prefix . $values) or die($conn->error);
$counter = 0;
$values ="";
$batch++;
}
}
if ($counter > 0) { // Execute the last batch
$values = substr($values, 0, strlen($values) - 1);
$conn->query($sql_prefix . $values) or die($conn->error);
}
// Output results
$time_end = microtime(true);
$time = $time_end - $time_start;
echo "Importing Script running at: $date <br/>";
echo "Timezone: $timezone <br/>";
echo "<br/>";
echo "Script Summary:";
echo "Time running script: " . round($time,3) . " seconds <br/>";
echo "Memory: ".memory_get_usage() . " bytes <br/>";
echo "Records Updated: $lines <br/>";
echo "Batches run: $batch <br/>";
?>
date\u default\u timezone\u set('Australia/Brisbane');
$date=date('m/d/Y h:i:sa',time());
$timezone=date\u default\u timezone\u get();
$time\U start=微时间(真);
$batch_size=500;//每批要读取的行数
$batch=0;
$counter=0;
$lines=0;
$conn=newmysqli($servername、$username、$password、$dbname);
如果($conn->connect\u错误){
die(“连接失败:”.$conn->connect\U错误);
}
//从表中删除现有数据
$sql=“TRUNCATE TABLE`workorderstest`”;
$conn->query($sql);
$handle=fopen(dirname(_文件)。“/files/workorders.csv”,“r”);
//而不是逐个执行查询,
//让我们准备一个SQL查询,它将插入批处理中的所有值
$sql_prefix=“插入到WorkOrderTest(id、parentid、f1、f2、f3、f4、f5、f6、f7、f8、f9、f10)值中”;
$values=“”;
while(($line=fgets($handle))!==false){
$values.=“($line),”;
$counter++;
$lines++;
如果($counter==$batch\u size){
$values=substr($values,0,strlen($values)-1);
$conn->query($sql\u前缀.$values)或die($conn->error);
$counter=0;
$values=“”;
$batch++;
}
}
如果($counter>0){//执行最后一批
$values=substr($values,0,strlen($values)-1);
$conn->query($sql\u前缀.$values)或die($conn->error);
}
//输出结果
$time\U end=微时间(真);
$time=$time\U end-$time\U start;
echo“导入运行于:$date
的脚本”;
echo“时区:$Timezone
”;
回声“
”;
回声“脚本摘要:”;
回显“时间运行脚本:”。四舍五入($time,3)。“秒
”;
echo“Memory:”.Memory\u get\u usage()。“字节
”;
echo“更新记录:$lines
”;
回显“批次运行:$batch
”;
?>
fgets()?这是一个很好的方法。另一个选项是使用file()
将整个文件读入数组,然后使用foreach()
在数组上循环
0
,并将查询字符串设置回初始值。最后,在循环结束时,您将需要使用剩余值执行查询(除非文件大小是批大小的精确倍数,在这种情况下,将不会有任何剩余值)这是对轮子的一点改造。mysql有一个非常快速高效的系统将CSV数据加载到表中。如果您对用户帐户拥有正确的权限,就可以从PHP内部调用代码。加载数据内置了跳过N行的支持
$path = dirname(__FILE__)."/files/workorderstest.csv";
$q = "LOAD DATA INFILE ? INTO TABLE workorderstest IGNORE ? LINES";
$stmt = $dbh->prepare($q);
$stmt->bindParam(1,"$dirname");
$stmt->bindParam(2,"$n");
$stmt->execute();
那是非常宝贵的几行代码,不是吗
请注意,此代码使用“忽略行”关键字跳过CSV中的行。你也可以使用IGNORE关键字,例如
LOAD DATA INFILE ? IGNORE INTO TABLE ....
由于pdo和prepared语句,我增加了这个选项,不幸的是,由于服务器的安全性,我不能使用LOAD DATA INFILE。否则,这将是一个优雅的解决方案。在我的示例中,它允许我输出更新的记录数等内容,我不确定LOAD DATA Infle是否在响应中包含这些内容。是的,如果没有权限,很遗憾,LOAD DATA无法使用。如果您能够使用它,它将返回插入的行数。谢谢你,先生,你是位绅士和学者。这正是我想要的。对于一个100meg的文件,当前的结果刚刚超过60秒,有1676579条记录更新。
$path = dirname(__FILE__)."/files/workorderstest.csv";
$q = "LOAD DATA INFILE ? INTO TABLE workorderstest IGNORE ? LINES";
$stmt = $dbh->prepare($q);
$stmt->bindParam(1,"$dirname");
$stmt->bindParam(2,"$n");
$stmt->execute();
LOAD DATA INFILE ? IGNORE INTO TABLE ....