रेल्स: शामिल: बनाम: मिलती है


345

यह एक "क्यों चीजें इस तरह से काम करती हैं" सवाल के बजाय "मैं नहीं जानता कि यह कैसे करना है" सवाल है ...

इसलिए संबंधित रिकॉर्ड जो आप जानते हैं कि आप उपयोग करने जा रहे हैं, को खींचने के लिए सुसमाचार का उपयोग करना है :includeक्योंकि आपको एक जुड़ाव मिलेगा और अतिरिक्त प्रश्नों का एक पूरा गुच्छा बचना होगा:

Post.all(:include => :comments)

हालाँकि जब आप लॉग देखते हैं, तो कोई जॉइन नहीं हो रहा है:

Post Load (3.7ms)   SELECT * FROM "posts"
Comment Load (0.2ms)   SELECT "comments.*" FROM "comments" 
                       WHERE ("comments".post_id IN (1,2,3,4)) 
                       ORDER BY created_at asc) 

यह है एक शॉर्टकट लेने क्योंकि यह एक ही बार में सभी टिप्पणियां खींचती है, लेकिन यह अभी भी एक में शामिल नहीं है (जो क्या सभी दस्तावेज़ कहने लगता)। एकमात्र तरीका जो मुझे मिल सकता है वह :joinsइसके बजाय उपयोग करना है :include:

Post.all(:joins => :comments)

और लॉग दिखाते हैं:

Post Load (6.0ms)  SELECT "posts".* FROM "posts" 
                   INNER JOIN "comments" ON "posts".id = "comments".post_id

क्या मैं कुछ भूल रहा हूँ? मेरे पास आधा दर्जन संघों के साथ एक ऐप है और एक स्क्रीन पर मैं उन सभी से डेटा प्रदर्शित करता हूं। ऐसा लगता है कि 6 व्यक्तियों के बजाय एक जॉइन-एड क्वेरी होना बेहतर होगा। मुझे पता है कि प्रदर्शन-वार यह हमेशा व्यक्तिगत प्रश्नों के बजाय जुड़ने के लिए बेहतर नहीं है (वास्तव में यदि आप समय व्यतीत कर रहे हैं, तो ऐसा लगता है कि ऊपर दिए गए दो व्यक्तिगत प्रश्न जुड़ने की तुलना में तेज़ हैं), लेकिन सभी डॉक्स के बाद मैं पढ़ रहा हूँ मैं :includeविज्ञापित के रूप में काम नहीं करते देख हैरान हूँ ।

हो सकता है कि रेल है प्रदर्शन मुद्दे के जानकार और कुछ मामलों को छोड़कर में शामिल नहीं करता है?


3
यदि आप रेल के पुराने संस्करण का उपयोग कर रहे हैं, तो कृपया बताएं कि टैग के माध्यम से या आपके प्रश्न निकाय में। अन्यथा, यदि आप रेल 4 का उपयोग कर रहे हैं, तो यह includes( इसे पढ़ने वाले किसी के लिए)
onebree

इसके अलावा अब है: प्रीलोड और: उत्सुक_लोड blog.bigbinary.com/2013/07/01/…
CJW

जवाबों:


179

ऐसा प्रतीत होता है कि :includeकार्यक्षमता 2.1 के साथ बदल दी गई थी। रेल सभी मामलों में शामिल हो जाती थी, लेकिन प्रदर्शन कारणों से कुछ परिस्थितियों में कई प्रश्नों का उपयोग करने के लिए इसे बदल दिया गया था। फैबियो अकिता के इस ब्लॉग पोस्ट में बदलाव के बारे में कुछ अच्छी जानकारी है ("अनुकूलित ईगर लोडिंग" शीर्षक अनुभाग देखें)।



यह बहुत मददगार है, धन्यवाद। मैं चाहता हूं कि रेल को एक 'जहां' की आवश्यकता के बिना भी शामिल होने के लिए मजबूर करने का एक तरीका था। कुछ मामलों में, आप जानते हैं कि जुड़ाव अधिक कुशल होगा और दोहराव का जोखिम नहीं उठाएगा।
जोनाथन स्वार्ट्ज

1
इन्हें भी देखें: blog.bigbinary.com/2013/07/01/…
नाथन लॉन्ग

@JonathanSwartz नए संस्करण रेल इस का उपयोग कर समर्थन की तरह लग रहा eagerload । इस लिंक के लिए धन्यवाद NathanLong
rubyprince

92

.joinsबस तालिकाओं में शामिल हो जाता है और बदले में चयनित क्षेत्रों को लाता है। यदि आप जुड़ने वाले क्वेरी परिणाम में संघों को बुलाते हैं, तो यह डेटाबेस के प्रश्नों को फिर से आग देगा

:includesउत्सुक रूप से शामिल संघों को लोड करेगा और उन्हें मेमोरी में जोड़ेगा। :includesसभी शामिल तालिकाओं को लोड करता है। यदि आप क्वेरी परिणाम को शामिल करने के लिए संघों को बुलाते हैं, तो यह किसी भी प्रश्न को आग नहीं देगा


71

जुड़ने और शामिल करने के बीच का अंतर यह है कि इसमें शामिल विवरण का उपयोग करने से अन्य तालिका (s) से सभी विशेषताओं में मेमोरी में लोड होने वाली एक बहुत बड़ी SQL क्वेरी उत्पन्न होती है।

उदाहरण के लिए, यदि आपके पास टिप्पणियों से भरी एक तालिका है और आप a: joins => का उपयोग करते हैं, तो उपयोगकर्ता सभी उपयोगकर्ता जानकारी को छाँटने के उद्देश्य से खींच सकते हैं, आदि यह ठीक काम करेगा और इससे कम समय लेगा: इसमें शामिल हैं, लेकिन यह कहें कि आप प्रदर्शित करना चाहते हैं उपयोगकर्ताओं के नाम, ईमेल, आदि के साथ टिप्पणी का उपयोग करके जानकारी प्राप्त करने के लिए: जुड़ता है, इसे लाने वाले प्रत्येक उपयोगकर्ता के लिए अलग-अलग SQL प्रश्न बनाने होंगे, जबकि यदि आपने उपयोग किया है: तो यह जानकारी उपयोग के लिए तैयार है।

महान उदाहरण:

http://railscasts.com/episodes/181-include-vs-joins


55

मैं हाल ही में :joinsऔर :includesरेल के बीच अंतर पर अधिक पढ़ रहा था । यहाँ एक व्याख्या है जो मुझे समझ में आया (उदाहरणों के साथ :))

इस परिदृश्य पर विचार करें:

  • एक उपयोगकर्ता has_many टिप्पणियाँ और एक टिप्पणी एक उपयोगकर्ता के अंतर्गत आता है।

  • उपयोगकर्ता मॉडल में निम्नलिखित विशेषताएं हैं: नाम (स्ट्रिंग), आयु (पूर्णांक)। टिप्पणी मॉडल में निम्नलिखित विशेषताएं हैं: सामग्री, user_id। एक टिप्पणी के लिए एक user_id अशक्त हो सकता है।

में शामिल:

: जुड़ने से दो तालिकाओं के बीच एक आंतरिक जुड़ाव होता है। इस प्रकार

Comment.joins(:user)

#=> <ActiveRecord::Relation [#<Comment id: 1, content: "Hi I am Aaditi.This is my first   comment!", user_id: 1, created_at: "2014-11-12 18:29:24", updated_at: "2014-11-12 18:29:24">, 
     #<Comment id: 2, content: "Hi I am Ankita.This is my first comment!", user_id: 2, created_at: "2014-11-12 18:29:29", updated_at: "2014-11-12 18:29:29">,    
     #<Comment id: 3, content: "Hi I am John.This is my first comment!", user_id: 3, created_at: "2014-11-12 18:30:25", updated_at: "2014-11-12 18:30:25">]>

लायेगा जहां user_id (टिप्पणी तालिका के) सभी रिकॉर्ड user.id (उन तालिका) के बराबर है। इस प्रकार यदि आप

Comment.joins(:user).where("comments.user_id is null")

#=> <ActiveRecord::Relation []>

आपको दिखाए गए अनुसार एक खाली सरणी मिलेगी।

इसके अलावा जॉइन मेमोरी में शामिल टेबल को लोड नहीं करता है। इस प्रकार यदि आप

comment_1 = Comment.joins(:user).first

comment_1.user.age
#=>←[1m←[36mUser Load (0.0ms)←[0m  ←[1mSELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1←[0m  [["id", 1]]
#=> 24

जैसा कि आप देख रहे हैं, comment_1.user.ageपरिणाम प्राप्त करने के लिए पृष्ठभूमि में एक डेटाबेस क्वेरी को फिर से आग देगा

शामिल हैं:

: इसमें दो तालिकाओं के बीच एक बाहरी बाहरी जुड़ाव शामिल है । इस प्रकार

Comment.includes(:user)

#=><ActiveRecord::Relation [#<Comment id: 1, content: "Hi I am Aaditi.This is my first comment!", user_id: 1, created_at: "2014-11-12 18:29:24", updated_at: "2014-11-12 18:29:24">,
   #<Comment id: 2, content: "Hi I am Ankita.This is my first comment!", user_id: 2, created_at: "2014-11-12 18:29:29", updated_at: "2014-11-12 18:29:29">,
   #<Comment id: 3, content: "Hi I am John.This is my first comment!", user_id: 3, created_at: "2014-11-12 18:30:25", updated_at: "2014-11-12 18:30:25">,    
   #<Comment id: 4, content: "Hi This is an anonymous comment!", user_id: nil, created_at: "2014-11-12 18:31:02", updated_at: "2014-11-12 18:31:02">]>

टिप्पणियों तालिका से सभी रिकॉर्ड के साथ एक शामिल तालिका में परिणाम होगा इस प्रकार यदि आप

Comment.includes(:user).where("comment.user_id is null")
#=> #<ActiveRecord::Relation [#<Comment id: 4, content: "Hi This is an anonymous comment!", user_id: nil, created_at: "2014-11-12 18:31:02", updated_at: "2014-11-12 18:31:02">]>

यह रिकॉर्ड प्राप्त करेगा जहां टिप्पणियों.user_id को दिखाया गया है।

इसके अलावा मेमोरी में दोनों तालिकाओं को लोड करता है। इस प्रकार यदि आप

comment_1 = Comment.includes(:user).first

comment_1.user.age
#=> 24

जैसा कि आप टिप्पणी कर सकते हैं_1.user.age बस पृष्ठभूमि में एक डेटाबेस क्वेरी फायरिंग के बिना मेमोरी से परिणाम लोड करता है।


क्या यह रेल 4 के लिए है?
वनबरी

@ हंटरसेवेंस: हाँ यह है
आदिती जैन

54

प्रदर्शन के विचारों के अलावा, एक कार्यात्मक अंतर भी है। जब आप टिप्पणियों में शामिल होते हैं, तो आप उन पोस्टों के लिए पूछ रहे हैं जिनमें टिप्पणियां हैं- डिफ़ॉल्ट रूप से एक आंतरिक जुड़ाव। जब आप टिप्पणी शामिल करते हैं, तो आप सभी पदों के लिए पूछ रहे हैं- एक बाहरी जुड़ाव।


10

tl; डॉ

मैं उन्हें दो तरीकों से उलट देता हूं:

जुड़ता है - रिकॉर्ड के सशर्त चयन के लिए।

शामिल है - जब एक परिणाम सेट के प्रत्येक सदस्य पर एक संघ का उपयोग कर।

लंबा संस्करण

डेटाबेस से आने वाले परिणाम सेट को फ़िल्टर करने के लिए जॉइन किया जाता है। आप इसका उपयोग अपनी मेज पर संचालन करने के लिए करते हैं। इसे एक ऐसे खंड के रूप में सोचें जो सेट सिद्धांत को निष्पादित करता है।

Post.joins(:comments)

के समान है

Post.where('id in (select post_id from comments)')

सिवाय इसके कि अगर एक से अधिक टिप्पणियाँ हैं, तो आपको डुप्लिकेट पोस्ट वापस मिलेंगे। लेकिन हर पोस्ट एक ऐसी पोस्ट होगी जिसमें टिप्पणियां हैं। आप इसे विशिष्ट के साथ ठीक कर सकते हैं:

Post.joins(:comments).count
=> 10
Post.joins(:comments).distinct.count
=> 2

अनुबंध में, includesविधि केवल यह सुनिश्चित करेगी कि संबंध को संदर्भित करते समय कोई अतिरिक्त डेटाबेस प्रश्न नहीं हैं (ताकि हम n + 1 अंक न बनाएं)

Post.includes(:comments).count
=> 4 # includes posts without comments so the count might be higher.

नैतिक है, का उपयोग करें joinsजब आप सशर्त सेट संचालन करना चाहते हैं और उपयोग करें includesजब आप किसी संग्रह के प्रत्येक सदस्य पर एक संबंध का उपयोग करने जा रहे हैं।


जो distinctमुझे हर बार मिलता है। धन्यवाद!
बेन हल

4

.joins डेटाबेस में शामिल होने के रूप में काम करता है और यह दो या अधिक टेबल से जुड़ता है और बैकेंड (डेटाबेस) से चयनित डेटा प्राप्त करता है।

.includes डेटाबेस के बाएं हाथ के रूप में काम करते हैं। यह बाईं ओर के सभी रिकॉर्डों को लोड करता है, इसमें राइट हैंड साइड मॉडल की प्रासंगिकता नहीं है। इसका उपयोग उत्सुक लोडिंग के लिए किया जाता है क्योंकि यह मेमोरी में सभी संबद्ध ऑब्जेक्ट को लोड करता है। यदि हम संघों को क्वेरी परिणाम में शामिल करते हैं, तो यह डेटाबेस पर किसी क्वेरी को फायर नहीं करता है, यह केवल मेमोरी से डेटा लौटाता है क्योंकि यह पहले से ही मेमोरी में डेटा लोड कर चुका है।


0

'जॉइन्स' का इस्तेमाल सिर्फ टेबल से जुड़ने के लिए किया जाता है और जब आपने जॉइनिंग पर एसोसिएशनों को फोन किया तो यह फिर से फायर क्वेरी होगी (इसका मतलब है कि कई क्वेश्चन क्लियर हो जाएंगे)

lets suppose you have tow model, User and Organisation
User has_many organisations
suppose you have 10 organisation for a user 
@records= User.joins(:organisations).where("organisations.user_id = 1")
QUERY will be 
 select * from users INNER JOIN organisations ON organisations.user_id = users.id where organisations.user_id = 1

it will return all records of organisation related to user
and @records.map{|u|u.organisation.name}
it run QUERY like 
select * from organisations where organisations.id = x then time(hwo many organisation you have)

SQL की कुल संख्या इस मामले में 11 है

लेकिन 'शामिल' के साथ शामिल संघों को उत्सुकता से लोड किया जाएगा और उन्हें मेमोरी में जोड़ा जाएगा (पहले लोड पर सभी संघों को लोड करें) और फिर से क्वेरी को फायर न करें

जब आप के साथ रिकॉर्ड मिलते हैं जैसे @ रिकॉर्ड्स = User.includes (: ऑर्गनाइज़ेशंस) .where ("organisations.user_id = 1") तो क्वेरी होगी

select * from users INNER JOIN organisations ON organisations.user_id = users.id where organisations.user_id = 1
and 


 select * from organisations where organisations.id IN(IDS of organisation(1, to 10)) if 10 organisation
and when you run this 

@ record.map {| u | u.organisation.name} कोई क्वेरी आग नहीं देगा

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