रेल में एक कॉल में कई वस्तुओं को सहेजना


93

मैं रेल में एक विधि है कि कुछ इस तरह कर रहा है:

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

समस्या यह है कि मुझे जितनी अधिक इकाइयाँ और अधिक समय लगना चाहिए। मुझे इस पर संदेह है क्योंकि यह हर रिकॉर्ड के लिए डेटाबेस को हिट करना है। चूंकि वे नेस्टेड हैं, मुझे पता है कि मैं माता-पिता को बचाने से पहले बच्चों को नहीं बचा सकता, लेकिन मैं एक बार में सभी माता-पिता को बचाना चाहूंगा, और फिर सभी बच्चों को। ऐसा करना अच्छा होगा:

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)

यह केवल दो डेटाबेस हिट में यह सब करेगा। वहाँ रेल में यह करने के लिए एक आसान तरीका है, या मैं एक समय में एक कर रहा हूँ?

जवाबों:


69

आप Foo.new के बजाय Foo.create का उपयोग करने का प्रयास कर सकते हैं। बनाएँ "एक ऑब्जेक्ट (या कई ऑब्जेक्ट्स) बनाता है और डेटाबेस में सहेजता है, यदि सत्यापन पास हो जाते हैं। परिणामस्वरूप ऑब्जेक्ट वापस कर दिया जाता है कि ऑब्जेक्ट डेटाबेस में सफलतापूर्वक सहेजा गया था या नहीं।"

आप इस तरह कई ऑब्जेक्ट्स बना सकते हैं:

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

फिर, प्रत्येक माता-पिता के लिए, आप इसकी संगति में जोड़ने के लिए बनाएँ का उपयोग कर सकते हैं:

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

मैं दोनों पढ़ने की सलाह देते ActiveRecord प्रलेखन और रेल गाइड पर ActiveRecord क्वेरी इंटरफ़ेस और ActiveRecord संघों । उत्तरार्द्ध में उन सभी तरीकों का एक मार्गदर्शक होता है, जब आप संघ की घोषणा करते हैं।


78
दुर्भाग्य से, ActiveRecord प्रति निर्मित मॉडल में एक INSERT क्वेरी उत्पन्न करेगा। OP एकल INSERT कॉल चाहता है, जो ActiveRecord नहीं करेगा।
फ्राँस्वा बेउसोलिल

हां, मैं यह सब एक सम्मिलित कॉल में प्राप्त करने की उम्मीद कर रहा था, लेकिन अगर एक्टिवरकॉर्ड वह स्मार्ट नहीं है, तो मुझे लगता है कि यह बहुत आसान नहीं है।
captncraig

@ FrançoisBeausoleil क्या आप सवाल stackoverflow.com/questions/15386450/… को देखते हुए बुरा मानेंगे , तो क्या यह होगा कि मैं एक ही समय में कई रिकॉर्ड नहीं डाल सकता?
रिचलेविस

3
यह सच है कि आप एक INSERT या UPDATE जेनरेट करने के लिए AR नहीं प्राप्त कर सकते हैं, लेकिन इसके साथ ActiveRecord::Base.transaction { records.each(&:save) }या समान रूप से आप कम से कम सभी INSERT या UPDATEs को एक ही लेनदेन में डाल सकते हैं।
युवल

1
दरअसल, ओपी डेटाबेस को कम हिट करना चाहता है, डीबी पहुंच को तेज करने के लिए, और ActiveRecord वास्तव में चलो आप ऐसा करते हैं, एक कॉल में सभी कॉल को एक लेन-देन में बैच कर। (देखें हरीश का जवाब, जिसे स्वीकार किया जाना चाहिए।) क्या ActiveRecord आपको ऐसा करने नहीं देगा, जिससे DB प्रति लेनदेन एक INSERT क्वेरी बना सके, लेकिन इससे कोई फर्क नहीं पड़ता, क्योंकि विलंबता नेटवर्क करने से आती है। डीबी तक पहुंच, और डीबी के अंदर ही नहीं जब यह INSERT प्रश्न करता है।
मैग्ने

99

चूंकि आपको कई आवेषण करने की आवश्यकता है, इसलिए डेटाबेस कई बार मारा जाएगा। आपके मामले में देरी इसलिए है क्योंकि प्रत्येक बचत अलग-अलग DB लेनदेन में की जाती है। आप एक लेन-देन में अपने सभी कार्यों को संलग्न करके विलंबता को कम कर सकते हैं।

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

आपकी सेव विधि इस तरह दिख सकती है:

# 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

saveपैरेंट ऑब्जेक्ट पर कॉल बच्चे वस्तुओं की बचत होती है।


3
मुझे इसकी ही खोज थी। मेरे बीजों को बहुत तेज करता है। धन्यवाद :-)
रेनरा जूल

16

आवेषण_आल (रेल 6+)

Rails 6एक नया तरीका insert_all शुरू किया , जो एकल में डेटाबेस में कई रिकॉर्ड सम्मिलित करता हैSQL INSERT स्टेटमेंट ।

साथ ही, यह विधि किसी भी मॉडल को तुरंत नहीं देती है और सक्रिय रिकॉर्ड कॉलबैक या सत्यापन को कॉल नहीं करती है।

इसलिए,

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

की तुलना में यह काफी अधिक कुशल है

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

यदि आप सब करना चाहते हैं तो नए रिकॉर्ड डालना है।


1
मैं तब तक इंतजार नहीं कर सकता जब तक हम अपने ऐप को अपडेट नहीं करते। रेल 6 में इतनी सारी ठंडी चीजें।
दान

1
एक बात ध्यान देने योग्य है: सम्मिलित करें_
sujay

11

दो जवाबों में से एक कहीं और मिला: बीयरलिंगटन द्वारा । वे दो आपके प्रदर्शन के लिए सर्वश्रेष्ठ शर्त हैं


मुझे लगता है कि एसक्यूएल का उपयोग करने के लिए आपका सबसे अच्छा दांव प्रदर्शन-वार होने वाला है, और प्रति प्रश्न कई पंक्तियाँ सम्मिलित करें। यदि आप एक INSERT स्टेटमेंट बना सकते हैं, जो कुछ ऐसा करता है:

INSERT INTO foos_bars (foo_id, bar_id) VALUES (1,1), (1,2), (1,3) .... आपको एक ही क्वेरी में हजारों पंक्तियाँ सम्मिलित करने में सक्षम होना चाहिए। मैंने आपका mass_habtm विधि आज़माया नहीं, लेकिन ऐसा लगता है कि आप कुछ इस तरह कर सकते हैं:


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

इसके अलावा, यदि आप "some_attribute" द्वारा बार खोज रहे हैं, तो सुनिश्चित करें कि आपके पास अपने डेटाबेस में अनुक्रमित फ़ील्ड है।


या

आप अभी भी activerecord- आयात पर एक नज़र हो सकता है। यह सही है कि यह एक मॉडल के बिना काम नहीं करता है, लेकिन आप सिर्फ आयात के लिए एक मॉडल बना सकते हैं।


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

चियर्स


यह सम्मिलित करने के लिए बहुत अच्छा काम करता है, लेकिन एक लेनदेन में कई रिकॉर्ड अपडेट करने के बारे में क्या?
अविशाई

2
अद्यतन करने के लिए आपको upsert का उपयोग करना चाहिए: github.com/seamusabshere/upsert । चीयर्स
गुयेन चिएन कांग

Sql क्वेरी के साथ बहुत खराब विचार। आपको ActiveRecord और लेन-देन का उपयोग करना चाहिए।
केरोजु

यह बुरा विचार नहीं है। अगर आप वन इंसर्ट कर रहे हैं, तो यह सफल होगा या असफल, लेन-देन की कोई आवश्यकता नहीं है, मुझे लगता है। या आप हमेशा उस एक को एक लेनदेन ब्लॉक में लपेट सकते हैं।
फर्नांडो फैब्रेटी

यह खराब रेल अभ्यास है
ब्लेयर एंडरसन

0

आपको इस रत्न "FastInserter" का उपयोग करने की आवश्यकता है -> https://github.com/joinhandshake/fast_inserter

और एक बड़ी संख्या और हजारों रिकॉर्ड सम्मिलित करना तेज़ है क्योंकि यह रत्न सक्रिय रिकॉर्ड को छोड़ देता है, और केवल एक एकल sql कच्ची क्वेरी का उपयोग करता है


1
हालाँकि, मणि का लिंक उपयोगी हो सकता है, कृपया कुछ कोड प्रदान करें, जो कि आस्कर अपने वर्तमान कोड के बजाय उपयोग कर सकते हैं (प्रश्न देखें)।
1919 को ट्रिनकोट


1
उत्तर के लिए आवश्यक जानकारी को एम्बेड करना होगा । कृपया अपने उत्तर को संपादित करें और वहां लिंक जोड़ें, और उत्तर के अंदर इसके आवश्यक भागों को भी जोड़ें, ताकि यह स्व-निहित हो।
त्रिनोट

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.