Saya memiliki metode di rel yang melakukan sesuatu seperti ini:
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
Masalahnya adalah ini membutuhkan waktu lebih lama dan lebih lama semakin banyak entitas yang saya tambahkan. Saya menduga ini karena harus mengenai database untuk setiap record. Karena mereka bersarang, saya tahu saya tidak bisa menyelamatkan anak-anak sebelum orang tua diselamatkan, tetapi saya ingin menyelamatkan semua orang tua sekaligus, dan kemudian semua anak-anak. Akan menyenangkan untuk melakukan sesuatu seperti:
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)
Itu akan melakukan semuanya hanya dalam dua klik database. Apakah ada cara mudah untuk melakukan ini di rel, atau apakah saya terjebak melakukannya satu per satu?
sumber
ActiveRecord::Base.transaction { records.each(&:save) }
atau serupa Anda setidaknya dapat menempatkan semua INSERT atau UPDATE ke dalam satu transaksi.Karena Anda perlu melakukan banyak penyisipan, database akan dipukul beberapa kali. Penundaan dalam kasus Anda adalah karena setiap penyimpanan dilakukan dalam transaksi DB yang berbeda. Anda dapat mengurangi latensi dengan memasukkan semua operasi Anda dalam satu transaksi.
class Foo belongs_to :parent, :class_name => "Foo" has_many :children, :class_name => "Foo", :foreign_key=> "parent_id" end
Metode penyimpanan Anda mungkin terlihat seperti ini:
# 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
The
save
panggilan pada objek induk menyimpan objek anak-anak.sumber
insert_all (Rel 6+)
Rails 6
memperkenalkan metode baru insert_all , yang menyisipkan banyak catatan ke dalam database dalam satuSQL INSERT
pernyataan.Selain itu, metode ini tidak membuat contoh model apa pun dan tidak memanggil callback atau validasi Rekaman Aktif.
Begitu,
Foo.insert_all([ { first_name: 'Jamie' }, { first_name: 'Jeremy' } ])
secara signifikan lebih efisien daripada
Foo.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])
jika semua yang ingin Anda lakukan adalah memasukkan record baru.
sumber
Salah satu dari dua jawaban ditemukan di tempat lain: oleh Beerlington . Keduanya adalah taruhan terbaik Anda untuk kinerja
Saya pikir kinerja terbaik Anda adalah menggunakan SQL, dan memasukkan banyak baris per kueri secara massal. Jika Anda dapat membuat pernyataan INSERT yang melakukan sesuatu seperti:
SISIPKAN KE foos_bars (foo_id, bar_id) VALUES (1,1), (1,2), (1,3) .... Anda harus dapat memasukkan ribuan baris dalam satu query. Saya tidak mencoba metode mass_habtm Anda, tetapi sepertinya Anda bisa melakukan sesuatu seperti:
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}")
Juga, jika Anda mencari Bilah dengan "some_attribute", pastikan Anda memiliki bidang yang diindeks di database Anda.
ATAU
Anda mungkin masih melihat activerecord-import. Benar bahwa itu tidak berfungsi tanpa model, tetapi Anda dapat membuat Model hanya untuk diimpor.
FooBar.import [:foo_id, :bar_id], [[1,2], [1,3]]
Bersulang
sumber
Anda perlu menggunakan permata ini "FastInserter" -> https://github.com/joinhandshake/fast_inserter
dan memasukkan sejumlah besar dan ribuan catatan cepat karena permata ini melewatkan catatan aktif, dan hanya menggunakan satu kueri mentah sql
sumber
Anda tidak perlu permata untuk mencapai DB dengan cepat dan hanya sekali!
Jackrg telah mengerjakannya untuk kami: https://gist.github.com/jackrg/76ade1724 E5E5E516292e4e
sumber