5

次の1回限りのレイクタスクで、ユーザーアカウント(数十万を超える)に基づくデータで埋め戻す必要がある新しいテーブルを作成しています。Railsで新しいテーブルをどのように埋め戻すべきですか?

私がしたことは、2000人のユーザーごとに大きなINSERT文字列を作成し、そのクエリを実行することです。

はここのコードはおおよそ次のようになります。だから私は思ったんだけど

task :backfill_my_new_table => :environment do 
    inserts = [] 
    User.find_each do |user| 
     tuple = # form the tuple based on user and user associations like (1, 'foo', 'bar', NULL) 
     inserts << tuple 
    end 

    # At this point, the inserts array is of size at least 20,000 
    conn = ActiveRecord::Base.connection 
    inserts.each_slice(2000) do |slice| 
     sql = "INSERT INTO my_new_table (ref_id, column_a, column_b, column_c) VALUES #{inserts.join(", ")}" 
     conn.execute(sql) 
    end 
end 

、これを行うには良い方法はありますか?私が取ったアプローチの欠点は何ですか?どのように改善するべきですか?もし私がinserts配列をスライスしておらず、数十万のVALUESタプルを持つ単一のINSERTを実行したらどうでしょう?その方法の欠点は何ですか?

ありがとうございます!

+0

なぜ、挿入を高速化するためにトランザクションでラップされたMyNewTableメソッドを使用しないのですか?また、現在の実装では、SQLインジェクションまであなたを開きます。 –

+0

ああ、私はあなたが一度に複数のインサートをやっているのを忘れていました。それは確かに高速です(しかし、もしあなたが普通のインサートをそれぞれ1000というトランザクションでラップするとどれくらいの大きさであるかはわかりません)。 –

答えて

0

は、あなたが使用しているPGのバージョンによって異なりますが、テーブルへのデータのバルク・ロードのほとんどの場合、これは十分なチェックリストです:

  • COPYの代わりに、INSERT、可能な限りを使用するようにしてください。
  • 複数のインサートを使用する場合、自動コミットを無効にし、単一のトランザクション内のすべてのインサートをラップ、すなわち、ターゲットテーブルのオン/ BEGIN; INSERT ...; INSERT ...; COMMIT;
  • 無効インデックスとチェック/制約。
  • テーブルのトリガーを無効にします。
  • alter table
  • ので、それは unloggedは(PG 9.5以降、データのインポート後のログオン有効にすることを忘れないでください)、または増加となりました max_wal_sizeので、文句を言わない行の

20Kは、このような大したことではありません浸水するWAL PGなので、の非常に複雑なトリガー/チェックが含まれていない限り、1つのトランザクション内の2kスライスインサートはうまくいくでしょう。また、PG manual section on bulk loadingと読む価値があります。

UPD:とa little bit old, yet wonderful piece from depesz、抜粋:

ので、あなたはできるだけ速くデータを挿入したい場合 - 使用コピー( - pgbulkloadいっそのことか)。何らかの理由でコピーを使用できない場合は、複数行挿入を使用します(8.2の新機能)!できれば、それらをトランザクションにバンドルし、準備されたトランザクションを使用しますが、一般的には、それほど多くを提供しません。

関連する問題