先日、とあるサイトの改修があって、その中でCSVをアップロードしてDBに取り込むという要望があった。既にこのサイトでは別の箇所でCSVアップロード登録を実装しておりメソッドもあるので超楽勝と思ってメソッドをそのまま使ったら・・・「超」はつかなかった。
ハマりポイントは、重複チェック。
ここがボトルネックになって5,000行の登録に30秒、10,000行で60秒という劇重ワールドになってしまった。
元々はこんな感じ。
foreach ( $csv_data as $line_num => $line ) {
$csv_key = [
'id' => $line['id'],
'title' => $line['title'],
'contents' => $line['contents'],
];
//CSVから読み込んだ内容を重複チェック用の配列に格納
$duplicate_check[] = $csv_key;
//重複チェック配列の要素数と重複を削除した重複チェック配列の要素数を比較し、違っていればエラー
if ( count( $duplicate_check ) != count( array_unique( $duplicate_check, SORT_REGULAR ) ) ) {
$duplicate_check = array_unique( $duplicate_check, SORT_REGULAR );
$errors[] = '重複行:' . $line_num + 1 . '[重複データ'. implode(',', $csv_key) .']';
}
}
array_unique()
で都度配列を作り直して、配列の個数を比較して、という真面目な(?)処理。でも、これだと少ない要素数のときは問題ないと思うけれども、データ件数が多くなってきたら大変なことになるよね、PHPは・・・。
ということで修正
で、さらにさらに、この部分ってDB登録用の重複を除外したデータを作るってことでなくて、もし重複があったらエラーメッセージを出力してオシマイなのです。
それならば、わざわざ都度配列を作り直さなくとも、単純にチェック用の連想配列を作って、isset()
で存在チェックしてやるくらいで問題無しです。
$csv_key = [
'id' => $line['id'],
'title' => $line['title'],
'contents' => $line['contents'],
];
$keys = implode( '-', $csv_key );
if ( isset( $duplicate_check[ $keys ] ) ) {
$errors[] = '重複行:' . $line_num + 1 . '[重複データ'. implode(',', $csv_key) .']';
} else {
$duplicate_check[ $keys ] = 1;
}
結果は・・・・5,000行で5秒、10,000行で10秒に!!!!
単純なチェックだったら、チェック用の連想配列作ってisset()
やarray_key_exists()
で十分だということでした。