• トップ
  • ブログ一覧
  • Rails 並列処理でCSVをDBにインサート:外部キー制約のエラーを回避する
  • Rails 並列処理でCSVをDBにインサート:外部キー制約のエラーを回避する

    はじめに

    大量のCSVデータをRailsのモデルを通じてデータベースにインサートする際に、外部キー制約(Foreign Key Constraints)によるエラーが発生したことはありませんか?この記事では、Railsアプリケーションで外部キー制約のエラーを回避する方法を説明します。

    背景

    Railsを使ったデータインポートでは、以下問題を解決しなくてはなりませんでした。

    • データインポート時に外部キー制約が原因でエラーが発生するのを回避

    発生事例

    エラーが発生するケース

    以下のコードは、並列処理を用いてCSVデータをインサートしていますが、外部キー制約のエラーが発生します。

    1require 'csv'
    2require 'parallel'
    3
    4table = "your_table" # インポート対象のテーブル名
    5files = ["file-1.csv", "file-2.csv", "file-3.csv", "file-4.csv"] # CSVファイル名リスト
    6
    7# 外部キー制約を無効化
    8TemporaryApplicationRecord.connection.execute("SET FOREIGN_KEY_CHECKS = 0")
    9
    10# テーブルをクリア
    11TemporaryApplicationRecord.connection.execute("TRUNCATE TABLE #{table}")
    12
    13def import_csv(file, table)
    14model = "Temporary::#{table.singularize.camelize}".constantize
    15CSV.foreach(file, headers: true).each_slice(1000) do |rows|
    16model.insert_all(rows.map { |row| row.to_h.transform_values { |v| v == "NULL" ? nil : v } })
    17end
    18end
    19
    20# 並列処理でCSVをインサート
    21Parallel.each(files, in_processes: 4) do |file|
    22import_csv(file, table)
    23end
    24
    25# 外部キー制約を有効化
    26TemporaryApplicationRecord.connection.execute("SET FOREIGN_KEY_CHECKS = 1)

    問題点

    • SET FOREIGN_KEY_CHECKS = 0 を1度だけ設定しており、各プロセスで共有されていません。

    エラーを防ぐコード

    1以下のコードは、外部キー制約のエラーを回避するように修正されています。
    2require 'csv'
    3require 'parallel'
    4
    5table = "your_table"
    6files = ["file-1.csv", "file-2.csv", "file-3.csv", "file-4.csv"]
    7
    8# テーブルをクリア
    9TemporaryApplicationRecord.connection.execute("TRUNCATE TABLE #{table}")
    10
    11def import_csv(file, table)
    12TemporaryApplicationRecord.connection.execute("SET FOREIGN_KEY_CHECKS = 0")
    13model = "Temporary::#{table.singularize.camelize}".constantize
    14CSV.foreach(file, headers: true).each_slice(1000) do |rows|
    15model.insert_all(rows.map { |row| row.to_h.transform_values { |v| v == "NULL" ? nil : v } })
    16end
    17TemporaryApplicationRecord.connection.execute("SET FOREIGN_KEY_CHECKS = 1")
    18end
    19
    20# 並列処理でCSVをインサート
    21Parallel.each(files, in_processes: 4) do |file|
    22import_csv(file, table)
    23end

    修正ポイント

    プロセスごとに外部キー制約を設定

    並列処理の中で、SET FOREIGN_KEY_CHECKS = 0 と SET FOREIGN_KEY_CHECKS = 1 を明示的に呼び出すことで、各プロセスが外部キー制約を無効化するように設定しました。

    詳細解説

    外部キー制約とは?

    MySQLや他のデータベースでの外部キー制約は、データの整合性を確保するために使用されます。しかし、大量のデータを一括でインポートする場合、整合性チェックがボトルネックとなり、パフォーマンスに影響を与えることがあります。

    SET FOREIGN_KEY_CHECKS = 0 を使用することで、外部キー制約のチェックを一時的に無効化することが可能です。ただし、これを正しく設定しないとエラーが発生することがあります。

    なぜエラーが発生するのか?

    並列処理を用いる場合、各プロセスは独立して実行されるため、1つのプロセスで無効化した外部キー制約が他のプロセスには適用されません。そのため、外部キー制約が有効な状態でデータがインサートされ、エラーが発生します。

    修正コードの効果

    修正版では、各プロセスで外部キー制約を明示的に無効化しているため、どのプロセスでも外部キー制約が原因のエラーが発生しません。

    まとめ

    大量データをインポートする際に外部キー制約のエラーを回避するには、以下のポイントを押さえることが大切です:

    • 並列処理の中で外部キー制約の無効化を設定する。

    この方法を適用することで、エラーを回避しながら大量データを効率的に処理できます。

    ライトコードでは、エンジニアを積極採用中!

    ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。

    採用情報へ

    かねまさ(エンジニア)
    かねまさ(エンジニア)
    Show more...

    おすすめ記事

    エンジニア大募集中!

    ライトコードでは、エンジニアを積極採用中です。

    特に、WEBエンジニアとモバイルエンジニアは是非ご応募お待ちしております!

    また、フリーランスエンジニア様も大募集中です。

    background