एक्टिवरकॉर्ड में उपश्रेणियाँ


81

एसक्यूएल के साथ मैं इस तरह के उप-प्रश्न आसानी से कर सकता हूं

User.where(:id => Account.where(..).select(:user_id))

यह उत्पादन करता है:

SELECT * FROM users WHERE id IN (SELECT user_id FROM accounts WHERE ..)

मैं रेल के 3 एक्टिवरकॉर्ड / एरीएल / मेटा_ प्लेस का उपयोग करके यह कैसे कर सकता हूं?

मुझे वास्तविक उपश्रेणियों की आवश्यकता है / चाहिए, कोई रूबी वर्कअराउंड (कई प्रश्नों का उपयोग करके) नहीं।

जवाबों:


126

रेल अब डिफ़ॉल्ट रूप से ऐसा करती है :)

Message.where(user_id: Profile.select("user_id").where(gender: 'm'))

निम्न SQL का उत्पादन करेगा

SELECT "messages".* FROM "messages" WHERE "messages"."user_id" IN (SELECT user_id FROM "profiles" WHERE "profiles"."gender" = 'm')

(संस्करण संख्या जो अब "संदर्भित करता है" सबसे अधिक संभावना है 3.2)


6
अगर हालत में नहीं है तो वही कैसे करें?
coorasse

13
@कोरस: यदि आप रेल 4 का उपयोग कर रहे हैं, तो अब एक notशर्त है । मैं इस पोस्ट में दृष्टिकोण को समायोजित करके रेल 3 में इसे पूरा करने में सक्षम था : subquery = Profile.select("user_id").where(gender: 'm')).to_sql; Message.where('user_id NOT IN (#{subquery})) मूल रूप से, ActiveRecordविधियों को पूरा करने के लिए उपयोग किया जाता है, ठीक से उद्धृत उप-वर्ग, जो तब बाहरी क्वेरी में अंतर्निर्मित होता है। मुख्य नकारात्मक पक्ष यह है कि सबक्वेरी पारम्स बाध्य नहीं हैं।
बारह17

3
रेल 4 के बारे में @ बारह17 के बिंदु को समाप्त करने के लिए, विशिष्ट नहीं वाक्यविन्यास है Message.where.not(user_id: Profile.select("user_id").where(gender: 'm'))- जो "नॉट इन" सबसिलेक्ट बनाता है। बस मेरी समस्या हल कर दी ..
स्टीव मिडग्ली ने

1
@ChristopherLindblom जब आप कहते हैं कि रेल अब "डिफ़ॉल्ट रूप से" करता है, तो वास्तव में आपका क्या मतलब है? रेल के रूप में 3.2? यह अच्छा होगा यदि हम यह कहने के लिए उत्तर बदल सकते हैं, "रेल्स एक्स संस्करण के रूप में डिफ़ॉल्ट रूप से ऐसा करता है"।
जेसन स्विट

@JasonSwett Im माफ करना मुझे पता नहीं है, यह शायद 3.2 था जैसा कि आप कहते हैं कि यह वर्तमान समय का संस्करण था और केवल रिलीज़ किए गए संस्करणों को चलाता था। भविष्य में प्रचलित होने वाली प्रूफिंग के बारे में सोचेंगे, इसे इंगित करने के लिए धन्यवाद।
क्रिस्टोफर लिंडब्लोम

43

Arel में, where()तरीके सरणियों को तर्क के रूप में ले सकते हैं जो "WHERE id IN ..." क्वेरी उत्पन्न करेगा। इसलिए आपने जो लिखा है वह सही लाइनों के साथ है।

उदाहरण के लिए, निम्नलिखित AREL कोड:

User.where(:id => Order.where(:user_id => 5)).to_sql

... जो इसके बराबर है:

User.where(:id => [5, 1, 2, 3]).to_sql

... PostgreSQL डेटाबेस पर निम्न SQL आउटपुट करेगा:

SELECT "users".* FROM "users" WHERE "users"."id" IN (5, 1, 2, 3)" 

अपडेट: टिप्पणियों के जवाब में

ठीक है, इसलिए मैंने सवाल गलत समझा। मेरा मानना ​​है कि आप चाहते हैं कि उप-क्वेरी स्पष्ट रूप से उन स्तंभ नामों को सूचीबद्ध करें जिन्हें डेटाबेस को दो प्रश्नों के साथ हिट नहीं करने के लिए चुना जाना है (जो कि ActiveRecord सबसे सरल स्थिति में है)।

आप उपयोग कर सकते हैं projectके लिए selectअपने में उप का चयन करें:

accounts = Account.arel_table
User.where(:id => accounts.project(:user_id).where(accounts[:user_id].not_eq(6)))

... जो निम्न SQL का उत्पादन करेगा:

SELECT "users".* FROM "users" WHERE "users"."id" IN (SELECT user_id FROM "accounts" WHERE "accounts"."user_id" != 6)

मुझे पूरी उम्मीद है कि मैंने आपको वही दिया है जो आप इस समय चाहते थे!


हां, लेकिन यह वही है जो मैं नहीं चाहता हूं क्योंकि यह दो अलग-अलग प्रश्न उत्पन्न करता है और एक भी नहीं जिसमें एक उपश्रेणी होती है।
गुच्ची

आपके प्रश्न की गलतफहमी के लिए क्षमा याचना। क्या आप एक उदाहरण दे सकते हैं कि आप अपने एसक्यूएल को किस तरह देखना चाहते हैं?
स्कॉट

कोई दिक्कत नहीं है। यह पहले से ही ऊपर उल्लेख किया गया है: उपयोगकर्ताओं से चयन करें * कहां से आईडी (चुनिंदा उपयोगकर्ता_दिए खातों से कहां ..)
गुच्ची

1
आह ठीक है। मुझे वही मिल रहा है जो आप अभी कह रहे हैं। मैं देखता हूं कि आपके द्वारा उत्पन्न 2 प्रश्नों का क्या मतलब है। सौभाग्य से, मुझे पता है कि आपकी समस्या को कैसे ठीक किया जाए! (देखें संशोधित उत्तर)
स्कॉट

23

मैं स्वयं इस प्रश्न का उत्तर ढूंढ रहा था, और मैं एक वैकल्पिक दृष्टिकोण के साथ आया था। मैंने सोचा था कि मैं इसे साझा करूँगा - आशा है कि यह किसी की मदद करता है! :)

# 1. Build you subquery with AREL.
subquery = Account.where(...).select(:id)
# 2. Use the AREL object in your query by converting it into a SQL string
query = User.where("users.account_id IN (#{subquery.to_sql})")

बिंगो! Bango!

रेल 3.1 के साथ काम करता है


4
यह पहली क्वेरी को दो बार निष्पादित करता है। यह करना बेहतर है subquery = Account.where(...).select(:id).to_sql query = User.where("users.account_id IN (#{subquery})")
coorasse

9
यह आपके REPL में केवल पहली क्वेरी को दो बार निष्पादित करेगा, क्योंकि इसे प्रदर्शित करने के लिए क्वेरी पर इसका कॉल to_s है। यह केवल आपके आवेदन में एक बार इसे निष्पादित करेगा।
रिची

क्या होगा अगर हम अकाउंट टेबल से कई कॉलम चाहते हैं?
अहमद हमजा

0

एक अन्य विकल्प:

Message.where(user: User.joins(:profile).where(profile: { gender: 'm' })

0

यह नेस्टेड सबक्वेरी का एक उदाहरण है जो रेल एक्टिवॉर्ड का उपयोग करके और जॉइन का उपयोग करते हुए किया जाता है, जहां आप प्रत्येक क्वेरी पर क्लॉस और परिणाम जोड़ सकते हैं:

आप अपनी मॉडल फ़ाइल में नेस्टेड इनर_क्वेरी और एक आउटर_क्वेरी स्कोप जोड़ सकते हैं और उपयोग कर सकते हैं ...

  inner_query = Account.inner_query(params)
  result = User.outer_query(params).joins("(#{inner_query.to_sql}) alias ON users.id=accounts.id")
   .group("alias.grouping_var, alias.grouping_var2 ...")
   .order("...")

गुंजाइश का एक उदाहरण:

   scope :inner_query , -> (ids) {
    select("...")
    .joins("left join users on users.id = accounts.id")
    .where("users.account_id IN (?)", ids)
    .group("...")
   }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.