Ищите более эффективный способ хранения данных CSV в моей базе данных - PullRequest
0 голосов
/ 04 марта 2020

Каждый день мне предоставляется файл .txt, содержащий данные, разделенные точкой с запятой. Пользователи моего приложения должны ежедневно загружать этот файл в базу данных.

В настоящее время я читаю и сохраняю информацию следующим образом:

$array = array();
$csv = str_getcsv($request->file, "\n");
foreach ($csv as &$row) {
    $row = str_getcsv($row, ";");
    $array[] = $row;
}
array_splice($array, 0, 1);

foreach ($array as &$row) {
    $query = Table::firstOrNew(['col2' => $row[1], 'col3' => $row[2]]);
    $query->col1 = $row[0];
    $query->col2 = $row[1];
    $query->col3 = $row[2];
    $query->col4 = $row[3];
    $query->col5 = $row[4];
    // [...]
    $query->col72 = $row[71];
    $query->col73 = $row[72];
    $query->save();
}

С этим методом связано то, что для успешной работы требуется слишком много времени (объем данных составляет около 5000 записей в день, что занимает ~ 2 минуты для заполнения приведенного выше кода). Как вы можете видеть, количество столбцов огромно, и данные должны быть прочитаны так, как будто они имеют дело с файлом .CSV, плюс я не могу вообще отказаться от любого из них.

Не говоря уже об увеличении этой проблемы в огромной степени, если по какой-то причине пользователь (или более) должен загрузить данные за несколько дней или даже за месяц .

Мне нужно найти лучший способ чтобы справиться с этой ситуацией. Я искал решение, но лучшее, что я смог найти, было то, что я должен использовать for l oop вместо foreach, что на самом деле не решило проблему.

Ответы [ 2 ]

0 голосов
/ 04 марта 2020

Будет ли возможность позволить базе данных сделать работу за вас?

LOAD DATA INFILE '/tmp/user_data.csv' INTO TABLE test FIELDS TERMINATED BY ';';

https://dev.mysql.com/doc/refman/8.0/en/load-data.html

Вы должны быть уверены, что CSV действителен, конечно.

0 голосов
/ 04 марта 2020

Вы проверяете для каждой строки, существует ли она и обновляется ли, если не вставляется, верно? Если это так, вы не можете оптимизировать этот код, чтобы он выполнялся быстрее, если у вас нет уникального столбца для каждой строки и запускаете необработанные запросы с помощью ON DUPLICATE KEY UPDATE, посмотрите это: Вставьте в таблицу MySQL или обновите, если существует

Второе решение состоит в том, чтобы удалить все старые записи, которые принадлежат этому файлу или пользователю, или некоторые уникальные, которые нельзя загрузить дважды, а затем вставить новые фрагменты строк методом insert, это будет намного быстрее. Пример

DB::beginTransaction();

try {
    Table::where('unique_file_rows_identified_column', $something)->delete();

    foreach(array_chunk($array, 1000) as $rows) {
        $rows_to_insert = [];
        foreach($rows as $row){
            $rows_to_insert[] = [
                'col1' => $row[0],
                'col2' => $row[1],
                'col3' => $row[2],
                'col4' => $row[3],
                'col5' => $row[4],
                // [...]
                'col72' => $row[71],
                'col73' => $row[72],
            ];
        }

        Table::insert($rows_to_insert);
    }
} catch (\Exception $e){ // If something went wrong and exception is thrown deleted rows will be restored
    DB::rollBack();
    dd($e->getMessage());
}
DB::commit();

Это выполнит только 5 запросов, если файл содержит 5000 строк и будет вставлен намного быстрее

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...