Работа с указателями файлов в 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.

  1. fgets() лучше всего использовать для этого?
  2. Нужно ли мне включать обратный вызов или что-то подобное, чтобы отложить обработку sql до тех пор, пока не будут прочитаны все строки?
  3. Я немного не знаю, с чего начать, так как я все еще изучаю 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/>";

?>

person Ryan    schedule 23.12.2016    source источник


Ответы (2)


Это своего рода переизобретение колеса. mysql имеет очень быструю и эффективную систему для загрузки данных CSV в таблицы. Это ЗАГРУЗИТЬ ДАННЫЕ В ФАЙЛЕ, если у вас есть соответствующие разрешения. в своей учетной записи пользователя вы можете вызывать код прямо из 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, например, g

LOAD DATA INFILE ? IGNORE INTO TABLE ....
person e4c5    schedule 23.12.2016
comment
Я увеличил это из-за pdo и подготовленных операторов, к сожалению, я не могу использовать LOAD DATA INFILE из-за безопасности сервера. В противном случае это было бы элегантным решением. В моем примере это позволяет мне выводить такие вещи, как количество обновленных записей, я не уверен, что LOAD DATA INFILE включает это в ответ. - person Ryan; 23.12.2016
comment
да, без разрешений, к сожалению, нельзя использовать данные загрузки. Однако, если вы можете его использовать, он вернет количество вставленных строк. Спасибо за + - person e4c5; 23.12.2016

person    schedule
comment
Вы, сэр, джентльмен и ученый. Это именно то, что я искал. Текущие результаты составляют чуть более 60 секунд для файла размером 100 мегабайт с 1 676 579 обновлениями записей. - person Ryan; 23.12.2016