Php 在csv上使用文件指针

Php 在csv上使用文件指针,php,mysql,csv,file-pointer,Php,Mysql,Csv,File Pointer,我想知道如何将下面的代码更改为只读取x行数,然后处理sql insert语句,然后继续按x行数读取文件,并一直处理到文件结束。我对文件指针的概念是陌生的,但我理解使用FGET应该是可能的 我希望将下面的代码改成一个函数,在这个函数中,我可以传递文件名和需要读取和处理的行数 我目前有: (来自) 我希望将内存占用保持在最低限度。我认为这应该只是跟踪指针->重复直到到达行->处理sql->从指针开始->重复直到eof fgets() 我是否需要执行回调或类似操作来延迟sql处理,直到读取所有行 我有

我想知道如何将下面的代码更改为只读取x行数,然后处理sql insert语句,然后继续按x行数读取文件,并一直处理到文件结束。我对文件指针的概念是陌生的,但我理解使用FGET应该是可能的

我希望将下面的代码改成一个函数,在这个函数中,我可以传递文件名和需要读取和处理的行数

我目前有: (来自)

我希望将内存占用保持在最低限度。我认为这应该只是跟踪指针->重复直到到达行->处理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/>";
    
    ?>
    
    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 ....