Raylarda tek bir aramada birden çok nesneyi kaydetme


93

Raylarda böyle bir şey yapan bir yöntemim var:

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

Sorun şu ki, ekledikçe daha fazla varlık daha uzun sürüyor. Bunun her kayıt için veritabanına vurması gerektiğinden şüpheleniyorum. Yuvalanmış olduklarından, ebeveynler kurtarılmadan çocukları kurtaramayacağımı biliyorum, ama bir kerede tüm ebeveynleri ve sonra tüm çocukları kurtarmak istiyorum. Şunun gibi bir şey yapmak güzel olurdu:

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)

Bu, hepsini yalnızca iki veritabanı isabetinde yapar. Bunu raylarda yapmanın kolay bir yolu var mı yoksa bunu teker teker yapmak zorunda mıyım?

Yanıtlar:


69

Foo.new yerine Foo.create kullanmayı deneyebilirsiniz. Create "Bir nesne (veya birden çok nesne) oluşturur ve doğrulamalar başarılı olursa veritabanına kaydeder. Sonuçta elde edilen nesne, nesnenin veritabanına başarıyla kaydedilip kaydedilmediğine bakılmaksızın döndürülür."

Bunun gibi birden çok nesne oluşturabilirsiniz:

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

Ardından, her ebeveyn için, ilişkilendirmesine eklemek için create özelliğini de kullanabilirsiniz:

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

Hem ActiveRecord belgelerini hem de ActiveRecord sorgu arabirimi ve ActiveRecord ilişkilendirmeleriyle ilgili Rails Kılavuzlarını okumanızı tavsiye ederim . İkincisi, bir ilişki bildirdiğinizde bir sınıfın kazandığı tüm yöntemlerin bir kılavuzunu içerir.


78
Maalesef ActiveRecord oluşturulan model başına bir INSERT sorgusu oluşturacaktır. OP, ActiveRecord'un yapmayacağı tek bir INSERT çağrısı istiyor.
François Beausoleil

Evet, hepsini bir ara aramada almayı umuyordum, ancak eğer aktif kayıt o kadar akıllı değilse, sanırım çok kolay değil.
captncraig

@ FrançoisBeausoleil stackoverflow.com/questions/15386450/… sorusuna bakar mısınız, bu yüzden aynı anda birden fazla kayıt ekleyemem mi?
Richlewis

3
Bir INSERT veya UPDATE oluşturmak için AR elde edemeyeceğiniz doğrudur, ancak ActiveRecord::Base.transaction { records.each(&:save) }veya benzeriyle en azından tüm INSERT'leri veya UPDATE'leri tek bir işleme koyabilirsiniz.
yuval

1
Aslında OP, veritabanı erişimini hızlandırmak için veritabanını daha az vurmak istiyor ve ActiveRecord, tüm çağrıları tek bir işlemde toplayarak bunu yapmanıza izin veriyor. (Kabul edilen cevap olması gereken Harish'in cevabına bakın.) ActiveRecord'un yapmanıza izin vermeyeceği şey, DB'nin işlem başına bir INSERT sorgusu oluşturmasını sağlamaktır, ancak gecikme ağın yapılmasından kaynaklandığı için bu çok da önemli değil. INSERT sorguları yaptığında DB'nin içine değil, DB'ye erişim.
Magne

99

Birden fazla ekleme yapmanız gerektiğinden, veritabanı birden çok kez vurulacaktır. Sizin durumunuzdaki gecikme, her kayıt işleminin farklı DB işlemlerinde yapılmasıdır. Tüm işlemlerinizi tek bir işlemde toplayarak gecikmeyi azaltabilirsiniz.

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

Kaydetme yönteminiz şöyle görünebilir:

# 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

saveAna nesne çağrı alt nesneleri kaydeder.


3
Tam da aradığım şey. Tohumlarımı çok hızlandırır. Teşekkürler :-)
Renra

16

insert_all (Rails 6+)

Rails 6tek bir ifadede veritabanına birden çok kayıt ekleyen yeni bir yöntem insert_all tanıttı SQL INSERT.

Ayrıca, bu yöntem herhangi bir modeli başlatmaz ve Aktif Kayıt geri aramalarını veya doğrulamalarını çağırmaz.

Yani,

Foo.insert_all([
  { first_name: 'Jamie' },
  { first_name: 'Jeremy' }
])

şundan önemli ölçüde daha etkilidir:

Foo.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }])

tek yapmak istediğiniz yeni kayıtlar eklemekse.


1
Uygulamamızı güncelleyene kadar bekleyemiyorum. Rails 6'da pek çok harika şey
Dan

2
dikkat edilmesi gereken bir şey şudur: insert_all, AR geri aramalarını ve doğrulamalarını atlar: edgeguides.rubyonrails.org/…
sujay

11

Başka bir yerde bulunan iki cevaptan biri: Beerlington tarafından . Bu ikisi performans için en iyi seçeneğiniz


Bence performans açısından en iyi seçeneğiniz SQL kullanmak ve sorgu başına birden çok satırı toplu olarak eklemek olacaktır. Şuna benzer bir şey yapan bir INSERT ifadesi oluşturabilirseniz:

INSERT INTO foos_bars (foo_id, bar_id) DEĞERLER (1,1), (1,2), (1,3) .... Tek bir sorguya binlerce satır girebilmelisiniz. Mass_habtm yönteminizi denemedim, ancak şöyle bir şey yapabilirsiniz gibi görünüyor:


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}")

Ayrıca, Bar'da "bir_öznitelik" ile arama yapıyorsanız, bu alanın veritabanınızda indekslendiğinden emin olun.


VEYA

Activerecord-import'a hala göz atabilirsiniz. Bir model olmadan çalışmadığı doğrudur, ancak yalnızca içe aktarma için bir Model oluşturabilirsiniz.


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

Şerefe


Bu, eklemek için harika çalışıyor, ancak bir işlemde birden çok kaydı güncellemek için ne dersiniz?
Avishai

2
Güncelleme için upsert kullanmalısınız: github.com/seamusabshere/upsert . cheers
Nguyen Chien Cong

Sql sorgusu ile çok zayıf bir fikir. ActiveRecord ve transaction'ı kullanmalısınız.
Kerozu

Fena fikir değil. ONE insert yapıyorsanız, ya başarılı ya da başarısız olur, işleme gerek yoktur, sanırım. Ya da bu BİR eki her zaman bir işlem bloğuna sarabilirsiniz.
Fernando Fabreti

bu kötü raylar uygulaması
Blair Anderson

0

bu cevheri kullanmanız gerekiyor "FastInserter" -> https://github.com/joinhandshake/fast_inserter

ve çok sayıda ve binlerce kayıt eklemek hızlıdır çünkü bu mücevher aktif kaydı atlar ve yalnızca tek bir ham sql sorgusu kullanır


1
Cevher bağlantısı yararlı olsa da, lütfen Soran'ın mevcut kodu yerine kullanabileceği bazı kodlar sağlayın (soruya bakın).
trincot


1
Cevapların temel bilgileri içermesi gerekir . Lütfen cevabınızı düzenleyin ve bağlantıyı oraya ekleyin ve ayrıca bağımsız olması için cevabın temel kısımlarını da ekleyin.
trincot

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.