wake-up-neo.com

Speichern mehrerer Objekte in einem Anruf in Schienen

Ich habe eine Methode in Rails, die so etwas tut:

a = Foo.new("bar")
a.save

b = Foo.new("baz")
b.save

...
x = Foo.new("123", :parent_id => a.id)
x.save

...
z = Foo.new("zxy", :parent_id => b.id)
z.save

Das Problem ist, dass dies länger und länger dauert, je mehr Elemente ich hinzufüge. Ich vermute, das liegt daran, dass es für jeden Datensatz die Datenbank treffen muss. Da sie verschachtelt sind, weiß ich, dass ich die Kinder nicht retten kann, bevor die Eltern gerettet sind, aber ich möchte alle Eltern auf einmal und dann alle Kinder retten. Es wäre schön, so etwas zu tun:

a = Foo.new("bar")
b = Foo.new("baz")
...
saveall(a,b,...)

x = Foo.new("123", :parent_id => a.id)
...
z = Foo.new("zxy", :parent_id => b.id)
saveall(x,...,z)

Das würde alles in nur zwei Datenbank-Hits tun. Gibt es eine einfache Möglichkeit, dies in Rails zu tun, oder stecke ich es nacheinander fest?

73
captncraig

Sie können versuchen, Foo.create anstelle von Foo.new zu verwenden. Erstellen "Erstellt ein Objekt (oder mehrere Objekte) und speichert es in der Datenbank, wenn die Validierungen bestanden werden. Das resultierende Objekt wird zurückgegeben, unabhängig davon, ob das Objekt erfolgreich in der Datenbank gespeichert wurde oder nicht."

Sie können mehrere Objekte wie folgt erstellen:

# Create an Array of new objects
  parents = Foo.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])

Dann können Sie für jedes übergeordnete Element auch create verwenden, um seiner Verknüpfung hinzuzufügen:

parents.each do |parent|
  parent.children.create (:child_name => 'abc')
end

Ich empfehle, die Dokumentation zu ActiveRecord und die Rails Guides auf ActiveRecord-Abfrage-Schnittstelle und ActiveRecord-Verknüpfungen zu lesen. Letzteres enthält einen Leitfaden für alle Methoden, die eine Klasse bei der Deklaration einer Assoziation erhält.

60
Roadmaster

Da Sie mehrere Einfügungen vornehmen müssen, wird die Datenbank mehrmals aufgerufen. Die Verzögerung in Ihrem Fall liegt darin, dass jede Sicherung in verschiedenen DB-Transaktionen erfolgt. Sie können die Latenzzeit reduzieren, indem Sie alle Ihre Vorgänge in einer Transaktion zusammenfassen.

class Foo
  belongs_to  :parent,   :class_name => "Foo"
  has_many    :children, :class_name => "Foo", :foreign_key=> "parent_id"
end

Ihre Speichermethode könnte so aussehen:

# build the parent and the children
a = Foo.new(:name => "bar")
a.children.build(:name => "123")

b = Foo.new("baz")
b.children.build(:name => "zxy")

#save parents and their children in one transaction
Foo.transaction do
  a.save!
  b.save!
end

Der Aufruf save für das übergeordnete Objekt speichert die untergeordneten Objekte.

77
Harish Shetty

Eine der beiden Antworten, die Sie anderswo gefunden haben: by Beerlington . Diese beiden sind Ihre beste Wahl für die Leistung


Ich denke, Ihre beste Wette ist es, SQL zu verwenden und mehrere Zeilen pro Abfrage einzufügen. Wenn Sie eine INSERT-Anweisung erstellen können, die Folgendes bewirkt:

INSERT INTO foos_bars (foo_id, bar_id) VALUES (1,1), (1,2), (1,3) .... Sie sollten Tausende von Zeilen in eine einzelne Abfrage einfügen können. Ich habe Ihre mass_habtm-Methode nicht ausprobiert, aber es scheint, als könnten Sie so etwas wie:


bars = Bar.find_all_by_some_attribute(:a) 
foo = Foo.create
values = bars.map {|bar| "(#{foo.id},#{bar.id})"}.join(",") 
connection.execute("INSERT INTO foos_bars (foo_id, bar_id) VALUES
#{values}")

Wenn Sie nach "some_attribute" suchen, stellen Sie sicher, dass dieses Feld in Ihrer Datenbank indiziert ist.


OR

Vielleicht haben Sie noch einen Blick auf activerecord-import. Es ist richtig, dass es ohne ein Modell nicht funktioniert, aber Sie könnten ein Modell nur für den Import erstellen.


FooBar.import [:foo_id, :bar_id], [[1,2], [1,3]]

Prost

8

Sie brauchen keinen Edelstein, um die DB schnell und nur einmal zu treffen!

Jackrg hat es für uns herausgefunden: https://Gist.github.com/jackrg/76ade1724bd816292e4e

0

sie müssen dieses Juwel "FastInserter" verwenden -> https://github.com/joinhandshake/fast_inserter

das Einfügen einer großen Anzahl und von Tausenden von Datensätzen ist schnell, da dieser Edelstein den aktiven Datensatz überspringt und nur eine einzige SQL-Abfrage verwendet

0
val caro