Log目次

【設計】CSVの処理について考える

作成日 2025-04-20更新日 2025-04-20

はじめに

CSVの機能を設計、実装する上でのアレコレを備忘録としてまとめました。

基本的なフォーマット

特別な理由がない限りは

出力項目はダブルクォーテーションをつけるのが安全だと思う

出力項目に記号(カンマなどCSV上で意味あるもの)が含まれている可能性があるので

避けた方がいい項目名

最初の項目名がID

https://dechnostick.hatenablog.com/entry/2015/02/02/022702

※現状も発生する問題かどうかは未検証

文字コード

(私の経験上)よく使われるのが上記2つ
Bom付きはExcelを使う想定の場合はつけておかないと
文字化けするので

1対多のデータ

1対多のデータを扱う場合
CSVのカラムを動的に増やすケースはあった
例えば、ある人の趣味をCSVに出力する場合は以下のような感じ

ユーザーID趣味1趣味2
野球サッカー

カラムの数は出力するデータによる
もし、趣味が5つ登録されている人がいたら趣味5まで出力される
趣味2までしか登録していない人は趣味3−5はブランク

趣味の一覧専用のCSVであれば、下記のような形式もある

ユーザーID趣味
野球
サッカー

※正直なところシステムで扱うことを考えたら、フォーマット(カンマ区切りなど)を決めた上で1つのカラムに複数の値を持たせたいが、Excelとかで編集しにくいと思う人もいると思うので、現実的ではないかもしれない

出力時間(タイムアウトの考慮)

CSVは大量のデータを出力するので処理時間も考慮しておいた方がいい

データが少ない場合は問題なくリアルタイムでCSVの作成->出力できていても
データが増えてきた場合にタイムアウトする可能性があるため(設定されている時間にもよるが)

SQLのチューニング、インデックスの考慮、アプリケーション上での無駄なループなど
処理時間を短くするために考えておくべきことはいくつかあるが
データ量や複雑なSQL(joinの数が多い、サブクエリがいっぱいあるなど)を
仕様上どうしても使用しなければいけないケースもあると思う

そうなると、リアルタイムでの出力ではなく
非同期処理も選択肢に入ってくる

例えば、画面でCSVのDLボタンを押下したときに
CSVを作成する非同期処理をキックして、画面へのレスポンスはすぐに返す
画面では、「CSV作成中です」など処理中であることがわかるメッセージを出しておく
※非同期処理のステータス(CSV作成中かどうか)を画面側で都度ポーリングするケースもあったし、 非同期処理のステータス一覧画面など別途作成するケースもあった

作成したCSVは、作成が終わったらメールで送信するケースやストレージ(S3など)に
アップして別途画面からDLするUIなど考えられる
メールだと容量には注意する必要がある

メモリ

大量データの処理なのでアプリケーション上のメモリには注意
railsならfind_eachなど

CSV取込に関して

大量データを取り込むのも時間がかかる(基本的には非同期処理で作るはず)

1行のデータ処理で更新するテーブルも複数に渡る場合もあるのでトランザクションが長くなりがち
(=ロックの時間が長くなる)
過去の案件では、CSVの1行1行は独立したデータ(もし他のデータがロールバックしてもコミット済みのデータには影響がないデータ)なので、 1行ごとにトランザクションをコミットする方針を取っていた
※パフォーマンスは落ちると思うが、1つ1つのトランザクションの時間は短くなるというメリットがある

トランザクション関連でもう一つ
マイクロサービス化している場合、ユーザー登録・更新時にその内容を別のサービスにAPIで連携する場合もあると思う
その場合は、APIの成功を持ってこちら側のトランザクションをコミットする
API連携の前にコミットしてしまうと、APIエラーになったときにこちら側がロールバックできずに、サービス間でのデータ不整合が発生してしまうこともある

railsでモデルのバリデーションでDBに対してマスタチェックをしていたら、1行の処理ごとにマスタチェックのSQLが実行されるのでパフォーマンス劣化の原因になることもある
例えば、名前の存在チェックする場合はモデルのバリデーションに頼るのではなく、取り込むCSVから名前の一覧を配列化して、DBに存在するか確認すればSQL一本で済む

可能であればバルクインサート、バルクアップデートができるように実装したいが、テーブルの親子関係的に親のIDが必要な場合もあるので実装してみると意外と難しい

他にもパフォーマンス向上のためCSV取込を並列で行うケースもあった
例えば、アップロードされたファイルの件数を確認して
1000行あるなら10並行でそれぞれ100行ずつ処理するといった感じ
並行して処理するのでCSVの各行の取込の順番に制限がないことが条件
※仮に同じファイルに同じレコードを更新する行があれば、あと勝ちになる。以前の現場では主にユーザーの作成、更新だったので特に問題にならなかった。

CSV取込時のエラーに関して

どの行のどの項目でなぜエラーになったかはユーザーが見える形にしておいた方がいい
じゃないと、ユーザー側でCSVの修正ができず
「なんかエラーになったけど理由わかりません」とお問い合わせに繋がり、 その調査で調査担当者が疲弊する