在 csv 上使用文件指针
Working with file pointers on a csv
我想知道如何更改下面的代码以读取 x 行数仅处理 sql 插入语句然后继续按 x 行读取文件并处理直到文件末尾。我对文件指针的想法很陌生,但我知道使用 fgets 应该是可能的。
我希望将下面的代码更改为一个函数,我可以在其中传递文件名和我想要读取和处理的行数。
我目前有:
(来自 )
$handle = fopen(dirname(__FILE__)."/files/workorderstest.csv" , "r");
$batch++;
if ($handle) {
$counter = 0;
//instead of executing query one by one,
//let us prepare 1 SQL query that will insert all values from the batch
$sql ="INSERT INTO workorderstest(id,parentid,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10) VALUES ";
while (($line = fgets($handle)) !== false) {
$sql .= "($line),";
$counter++;
}
$sql = substr($sql, 0, strlen($sql) - 1);
var_dump($sql);
if ($conn->query($sql) === TRUE) {
} else {
}
fclose($handle);
}
我想将内存占用保持在最低限度。我认为这应该只是跟踪指针的问题 -> 重复直到到达行 -> 处理 sql -> 从指针开始 -> 重复直到 eof.
fgets()
是最好的吗?
- 我是否需要结合回调或类似的回调来延迟 sql 处理,直到读取所有行?
- 因为我还在学习,所以我有点不知道从哪里开始 PHP。
**** 如果对其他人有帮助,请更新下面的回答脚本...
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/>";
?>
fgets()
是最好的方法吗? 这是一个很好的方法。另一种选择是使用 file()
将整个文件读入一个数组,然后使用 foreach()
.
遍历该数组
我需要合并回调吗?不需要。从文件中读取每批行后执行查询即可。
从哪里开始? 当计数器达到批大小时,执行查询。然后将计数器设置回 0
并将查询字符串设置回初始值。最后,在循环结束时,您需要使用剩余的值执行查询(除非文件大小是批处理大小的精确倍数,在这种情况下不会有任何剩余)。
$batch_size = 100;
$counter = 0;
//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++;
if ($counter == $batch_size) {
$values = substr($values, 0, strlen($values) - 1);
$conn->query($sql_prefix . $values) or die($conn->error);
$counter = 0;
$values ="";
}
}
if ($counter > 0) { // Execute the last batch
$values = substr($values, 0, strlen($values) - 1);
$conn->query($sql_prefix . $values) or die($conn->error);
}
这是对轮子的重新发明。 mysql 有一个非常快速和高效的系统来将 CSV 数据加载到表中。那就是 LOAD DATA INFILE 如果您对您的用户帐户拥有正确的权限,则可以从 PHP 中调用代码。并且 LOAD DATA 内置了对跳过 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();
这几行代码很珍贵,不是吗?
请注意,此代码使用 IGNORE LINES 关键字来跳过 CSV 中的行。您还可以使用 IGNORE 关键字 eg.g
LOAD DATA INFILE ? IGNORE INTO TABLE ....
我想知道如何更改下面的代码以读取 x 行数仅处理 sql 插入语句然后继续按 x 行读取文件并处理直到文件末尾。我对文件指针的想法很陌生,但我知道使用 fgets 应该是可能的。
我希望将下面的代码更改为一个函数,我可以在其中传递文件名和我想要读取和处理的行数。
我目前有:
(来自
$handle = fopen(dirname(__FILE__)."/files/workorderstest.csv" , "r");
$batch++;
if ($handle) {
$counter = 0;
//instead of executing query one by one,
//let us prepare 1 SQL query that will insert all values from the batch
$sql ="INSERT INTO workorderstest(id,parentid,f1,f2,f3,f4,f5,f6,f7,f8,f9,f10) VALUES ";
while (($line = fgets($handle)) !== false) {
$sql .= "($line),";
$counter++;
}
$sql = substr($sql, 0, strlen($sql) - 1);
var_dump($sql);
if ($conn->query($sql) === TRUE) {
} else {
}
fclose($handle);
}
我想将内存占用保持在最低限度。我认为这应该只是跟踪指针的问题 -> 重复直到到达行 -> 处理 sql -> 从指针开始 -> 重复直到 eof.
fgets()
是最好的吗?- 我是否需要结合回调或类似的回调来延迟 sql 处理,直到读取所有行?
- 因为我还在学习,所以我有点不知道从哪里开始 PHP。
**** 如果对其他人有帮助,请更新下面的回答脚本...
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/>";
?>
fgets()
是最好的方法吗? 这是一个很好的方法。另一种选择是使用file()
将整个文件读入一个数组,然后使用foreach()
. 遍历该数组
我需要合并回调吗?不需要。从文件中读取每批行后执行查询即可。
从哪里开始? 当计数器达到批大小时,执行查询。然后将计数器设置回
0
并将查询字符串设置回初始值。最后,在循环结束时,您需要使用剩余的值执行查询(除非文件大小是批处理大小的精确倍数,在这种情况下不会有任何剩余)。
$batch_size = 100;
$counter = 0;
//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++;
if ($counter == $batch_size) {
$values = substr($values, 0, strlen($values) - 1);
$conn->query($sql_prefix . $values) or die($conn->error);
$counter = 0;
$values ="";
}
}
if ($counter > 0) { // Execute the last batch
$values = substr($values, 0, strlen($values) - 1);
$conn->query($sql_prefix . $values) or die($conn->error);
}
这是对轮子的重新发明。 mysql 有一个非常快速和高效的系统来将 CSV 数据加载到表中。那就是 LOAD DATA INFILE 如果您对您的用户帐户拥有正确的权限,则可以从 PHP 中调用代码。并且 LOAD DATA 内置了对跳过 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();
这几行代码很珍贵,不是吗?
请注意,此代码使用 IGNORE LINES 关键字来跳过 CSV 中的行。您还可以使用 IGNORE 关键字 eg.g
LOAD DATA INFILE ? IGNORE INTO TABLE ....