ActiveRecord / रेल के साथ क्वेरी में नहीं व्यक्त करने के लिए कैसे?


207

बस इसे अपडेट करने के लिए क्योंकि ऐसा लगता है कि बहुत से लोग इस पर आते हैं, यदि आप ट्रान्स Lê` और VinniVidiVicci के जवाबों पर रेल 4 का उपयोग कर रहे हैं।

Topic.where.not(forum_id:@forums.map(&:id))

Topic.where(published:true).where.not(forum_id:@forums.map(&:id))

मुझे उम्मीद है कि एक आसान समाधान है जिसमें शामिल नहीं है find_by_sql, अगर नहीं तो मुझे लगता है कि काम करना होगा।

मुझे यह लेख मिला जो इसका संदर्भ देता है:

Topic.find(:all, :conditions => { :forum_id => @forums.map(&:id) })

जो जैसा है वैसा है

SELECT * FROM topics WHERE forum_id IN (<@forum ids>)

मैं सोच रहा हूं कि क्या ऐसा करने का कोई तरीका NOT INहै, जैसे:

SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)

3
FYI के रूप में, Datamapper को IN के लिए विशिष्ट समर्थन मिला है। उदाहरण:Person.all(:name.not => ['bob','rick','steve'])
मार्क थॉमस

1
अज्ञानी होने के लिए खेद है, लेकिन डाटामापर क्या है? रेल 3 का वह हिस्सा है?
टोबे जॉइनर

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

जवाबों:


313

रेल 4+:

Article.where.not(title: ['Rails 3', 'Rails 5']) 

रेल 3:

Topic.where('id NOT IN (?)', Array.wrap(actions))

actionsएक सरणी कहां है:[1,2,3,4,5]


1
यह नवीनतम सक्रिय रिकॉर्ड क्वेरी मॉडल के साथ उचित दृष्टिकोण है
नेविर

5
@NewAlexandria सही है, इसलिए आपको कुछ करना होगा Topic.where('id NOT IN (?)', (actions.empty? ? '', actions)। यह अभी भी शून्य पर टूट जाएगा, लेकिन मुझे लगता है कि जिस सरणी में आप गुजरते हैं वह आमतौर पर एक फिल्टर द्वारा उत्पन्न होता है []जो बहुत कम से कम वापस आएगा और कभी भी शून्य नहीं होगा। मैं स्क्वील को चेक करने की सलाह देता हूं, जो एक्टिव रिकॉर्ड के ऊपर एक डीएसएल है। तब आप कर सकते हैं: Topic.where{id.not_in actions}शून्य / खाली / या अन्यथा।
दनेनू

6
@danneu बस के लिए स्वैप .empty?करते हैं .blank?और आप शून्य प्रूफ हैं
कोलेन

(क्रियाएँ.empty? '', क्रियाएँ) @daaneu द्वारा किया जाना चाहिए (actions.empty? ''? क्रियाएँ)
marcel salathe

3
रेल 4 नोटेशन के लिए जाएं: Article.where.not (शीर्षक: ['रेल 3', 'रेल्स 5'])
ताल


50

आप कुछ इस तरह की कोशिश कर सकते हैं:

Topic.find(:all, :conditions => ['forum_id not in (?)', @forums.map(&:id)])

आपको करने की आवश्यकता हो सकती है @forums.map(&:id).join(',')। मुझे याद नहीं आ रहा है कि अगर यह अगम्य है तो रेल सीएसवी सूची में तर्क देगा।

आप यह भी कर सकते हैं:

# in topic.rb
named_scope :not_in_forums, lambda { |forums| { :conditions => ['forum_id not in (?)', forums.select(&:id).join(',')] }

# in your controller 
Topic.not_in_forums(@forums)

50

अरे का उपयोग करना:

topics=Topic.arel_table
Topic.where(topics[:forum_id].not_in(@forum_ids))

या, यदि पसंदीदा हो:

topics=Topic.arel_table
Topic.where(topics[:forum_id].in(@forum_ids).not)

और 4 के बाद से रेल:

topics=Topic.arel_table
Topic.where.not(topics[:forum_id].in(@forum_ids))

कृपया ध्यान दें कि अंततः आप नहीं चाहते कि मंच_आईडी आईडी सूची हो, बल्कि एक उपश्रेणी हो, यदि ऐसा है तो आपको विषयों को प्राप्त करने से पहले ऐसा कुछ करना चाहिए:

@forum_ids = Forum.where(/*whatever conditions are desirable*/).select(:id)

इस तरह से आपको एक ही क्वेरी में सब कुछ मिलता है: कुछ इस तरह:

select * from topic 
where forum_id in (select id 
                   from forum 
                   where /*whatever conditions are desirable*/)

यह भी ध्यान दें कि अंततः आप ऐसा नहीं करना चाहते हैं, बल्कि एक जुड़ाव है - जो अधिक कुशल हो सकता है।


2
एक जुड़ाव अधिक कुशल हो सकता है, लेकिन जरूरी नहीं। उपयोग सुनिश्चित करें EXPLAIN!
जेम्स

20

@ तुंग ल उत्तर पर विस्तार करने के लिए, रेल 4 में आप निम्न कार्य कर सकते हैं:

Topic.where.not(forum_id:@forums.map(&:id))

और आप इसे एक कदम आगे ले जा सकते हैं। यदि आपको केवल प्रकाशित विषयों के लिए पहले फ़िल्टर करने की आवश्यकता है और फिर उन आईडी को फ़िल्टर करें जिन्हें आप नहीं चाहते हैं, तो आप ऐसा कर सकते हैं:

Topic.where(published:true).where.not(forum_id:@forums.map(&:id))

रेल 4 यह इतना आसान बना देता है!


12

@forumsखाली होने पर स्वीकृत समाधान विफल हो जाता है। यह करने के लिए मुझे यह करना था

Topic.find(:all, :conditions => ['forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id))])

या, यदि रेल का उपयोग 3+:

Topic.where( 'forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id)) ).all

4

ऊपर दिए गए अधिकांश उत्तर आपको पर्याप्त होने चाहिए, लेकिन यदि आप इस तरह के कुछ अधिक जटिल और जटिल संयोजन कर रहे हैं, तो स्क्वील की जांच करें । आप ऐसा कुछ करने में सक्षम होंगे:

Topic.where{{forum_id.not_in => @forums.map(&:id)}}
Topic.where{forum_id.not_in @forums.map(&:id)} 
Topic.where{forum_id << @forums.map(&:id)}

2

आप Ernie Miller द्वारा meta_where प्लगइन पर एक नज़र रखना चाह सकते हैं । आपका SQL कथन:

SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)

... इस तरह व्यक्त किया जा सकता है:

Topic.where(:forum_id.nin => @forum_ids)

Railscasts के रयान बेट्स ने MetaWhere को समझाते हुए एक अच्छा स्क्रैंकास्ट बनाया

सुनिश्चित नहीं हैं कि यह वही है जो आप देख रहे हैं, लेकिन मेरी आँखों के लिए यह निश्चित रूप से एक एम्बेडेड SQL क्वेरी से बेहतर दिखता है।


2

मूल पोस्ट में विशेष रूप से संख्यात्मक आईडी का उपयोग करने का उल्लेख किया गया है, लेकिन मैं यहां स्ट्रिंग की एक सरणी के साथ NOT IN करने के लिए वाक्यविन्यास की तलाश में आया था।

ActiveRecord आपके लिए भी अच्छी तरह से हैंडल करेगा:

Thing.where(['state NOT IN (?)', %w{state1 state2}])

1

क्या इन फ़ोरम आईडी को व्यावहारिक तरीके से काम किया जा सकता है? जैसे आप इन मंचों को किसी तरह पा सकते हैं - अगर ऐसा है तो आपको कुछ ऐसा करना चाहिए

Topic.all(:joins => "left join forums on (forums.id = topics.forum_id and some_condition)", :conditions => "forums.id is null")

जो एसक्यूएल करने से ज्यादा कुशल होगा not in


1

यह तरीका पठनीयता के लिए अनुकूलन करता है, लेकिन यह डेटाबेस प्रश्नों के मामले में उतना कुशल नहीं है:

# Retrieve all topics, then use array subtraction to
# find the ones not in our list
Topic.all - @forums.map(&:id)

0

आप अपनी स्थितियों में sql का उपयोग कर सकते हैं:

Topic.find(:all, :conditions => [ "forum_id NOT IN (?)", @forums.map(&:id)])


0

जब आप किसी रिक्त सरणी को क्वेरी करते हैं, तो "<< 0" उस सरणी में जहां ब्लॉक में है इसलिए यह "NULL" वापस नहीं करता है और क्वेरी को तोड़ता है।

Topic.where('id not in (?)',actions << 0)

यदि कार्रवाई एक खाली या रिक्त सरणी हो सकती है।


1
चेतावनी: यह वास्तव में सरणी में 0 जोड़ता है, इसलिए यह अब खाली नहीं है। यदि आप इसे बाद में उपयोग करते हैं, तो सरणी को संशोधित करने का दोहरा प्रभाव भी है। एक बेहतर तरीके से इसे लपेटने के लिए बेहतर है और धार के मामलों के लिए Topic.none / सभी का उपयोग करें
टेड पेन्निंग्स

एक सुरक्षित तरीका है:Topic.where("id NOT IN (?)", actions.presence || [0])
वेस्टन गैंगर

0

यहाँ एक और अधिक जटिल है "क्वेरी में नहीं", स्क्वील का उपयोग करके रेल्स 4 में एक सबक्वेरी का उपयोग करना। बेशक समान चक्र की तुलना में बहुत धीमी है, लेकिन हे, यह काम करता है।

    scope :translations_not_in_english, ->(calmapp_version_id, language_iso_code){
      join_to_cavs_tls_arr(calmapp_version_id).
      joins_to_tl_arr.
      where{ tl1.iso_code == 'en' }.
      where{ cavtl1.calmapp_version_id == my{calmapp_version_id}}.
      where{ dot_key_code << (Translation.
        join_to_cavs_tls_arr(calmapp_version_id).
        joins_to_tl_arr.    
        where{ tl1.iso_code == my{language_iso_code} }.
        select{ "dot_key_code" }.all)}
    }

स्कोप में पहले 2 विधियाँ अन्य स्कोप हैं जो एलियास कैवल्ट 1 और टीएल 1 घोषित करती हैं। << स्क्वील में ऑपरेटर नहीं है।

आशा है कि यह किसी की मदद करता है।

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