在csv上使用文件指针

时间:2016-12-23 02:20:42

标签: php mysql csv file-pointer

我想知道如何更改下面的代码来读取x行只处理sql insert语句然后继续按x号读取文件并处理直到文件结束。我是文件指针的新手,但我知道应该可以使用fgets。

我希望将以下代码更改为一个函数,我可以传递文件名和我想要读取和处理的行数。

我目前有: (来自here

$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。

  1. fgets()最适合用于此目的吗?
  2. 我是否需要进行回调或其他类似的操作以推迟sql处理直到读取所有行?
  3. 我还有点迷失在哪里,因为我还在学习PHP。
  4. ****更新了以下答案脚本,如果它可以帮助其他人...

    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/>";
    
    ?>
    

2 个答案:

答案 0 :(得分:1)

  1. fgets()最好用吗?这是一个很好的方法。另一个选择是使用file()将整个文件读入数组,然后使用foreach()循环遍历数组。

  2. 我是否需要进行回调?否。只需在阅读文件中的每一批行后执行查询。

  3. 从何处开始?当计数器达到批量大小时,请执行查询。然后将计数器设置回0并将查询字符串设置回初始值。最后,在循环结束时,您需要使用剩余值执行查询(除非文件大小是批量大小的精确倍数,在这种情况下,不会有任何剩余的值)。

  4. $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);
    }
    

答案 1 :(得分:1)

这对他的轮子有点重塑。 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 ....