5

Ich erstelle eine neue Tabelle, die mit Daten basierend auf Benutzerkonten (über ein paar Dutzend) mit der folgenden einmaligen Rake-Aufgabe gefüllt werden muss.Wie sollten Sie eine neue Tabelle in Rails füllen?

Ich habe beschlossen, eine große INSERT-Zeichenfolge für jede 2000 Benutzer zu erstellen und diese Abfrage auszuführen.

Hier ist, was der Code in etwa wie folgt aussieht:

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 

Ich frage mich also, gibt es einen besseren Weg, dies zu tun? Was sind die Nachteile meines Ansatzes? Wie soll ich es verbessern? Was, wenn ich das inserts Array nicht zerstückelte und einfach ein einziges INSERT mit über ein paar Dutzend tausend VALUES-Tupeln ausführte? Was sind die Nachteile dieser Methode?

Danke!

+0

Warum sollten Sie die MyNewTable-Methoden, die in einer Transaktion enthalten sind, nicht verwenden, um die Einfügungen zu beschleunigen? Auch die aktuelle Implementierung öffnet Sie bis zur SQL-Injektion. –

+0

Oh, ich habe verpasst, dass du mehrere Inserts auf einmal machst. Das wäre in der Tat schneller (aber nicht sicher, wie viel, wenn Sie normale Beilagen in einer Transaktion von etwa 1000 verpackt haben. –

Antwort

0

Abhängig von dem PG-Version Sie verwenden, aber in den meisten Fällen von Bulkbeladung Daten in eine Tabelle ist dies genug, um Checkliste:

  • Versuch COPY statt INSERT zu verwenden, wann immer möglich;
  • Wenn Sie mehrere INSERTs verwenden, deaktivieren Sie Autocommit und umschließen Sie alle INSERTs in einer einzigen Transaktion, d. H. BEGIN; INSERT ...; INSERT ...; COMMIT;
  • Deaktivieren Sie Indizes und Prüfungen/Einschränkungen auf/von einer Zieltabelle;
  • Tabellenauslöser deaktivieren;
  • alter table so wurde es unlogged (seit PG 9.5, nicht nach dem Datenimport Anmeldung zu drehen vergessen) oder erhöhen max_wal_size so WAL wont

20k Zeilen überflutet wird, ist nicht so eine große Sache für ein PG, also 2k-slice Inserts innerhalb einer Transaktion sind in Ordnung, es sei denn, es sind einige sehr komplexe Trigger/Checks beteiligt. Es lohnt sich auch, PG manual section on bulk loading zu lesen.

UPD: und a little bit old, yet wonderful piece from depesz, Auszug:

so, wenn Sie Daten einfügen, so schnell wie möglich - Einsatz Kopie (oder besser noch - pgbulkload). Wenn Sie aus irgendeinem Grund keine Kopie verwenden können, verwenden Sie mehrzeilige Einfügungen (neu in 8.2!). dann, wenn Sie können, bündeln Sie sie in Transaktionen, und verwenden Sie vorbereitete Transaktionen, aber im Allgemeinen - sie geben Ihnen nicht viel.