2012-10-18 12 views
26

मेरे पास लगभग 100 उपयोगकर्ताओं की एक तालिका है और मेरे पास उपयोगकर्ता आईडी की एक सरणी भी है। मैं जो करना चाहता था वह उन सभी उपयोगकर्ताओं को दिखाता है जो उपयोगकर्ता आईडी के इस सरणी का हिस्सा नहीं हैं। जब मैं इसखाली सरणी के साथ रिकॉर्ड्स पुनर्प्राप्त करते समय समस्या

User.where('id NOT IN (?)', [9, 2, 3, 4]) 

की तरह कुछ करना यह सफलतापूर्वक रिकॉर्ड जहां उपयोगकर्ता आईडी कि सरणी में संबंधित नहीं है देता है। लेकिन अगर कि सरणी की तरह खाली है तो

User.where('id NOT IN (?)', []) 

यह किसी भी उपयोगकर्ताओं को वापस वापस नहीं करता है और SQL क्वेरी कि ऐसा क्यों होता या यह एक बग हो सकता है किसी को भी पता है इस

SELECT "users".* FROM "users" WHERE (id NOT IN (NULL)) 

तरह लग रहा है? मैं PostgreSQL के साथ रेल 3.2.5 का उपयोग कर रहा हूँ।

+2

यह एक रेल मुद्दा या संभवतः एक 'Pg' मणि मुद्दे की तरह दिखता है; यह 'खाली' के रूप में एक खाली सरणी का इलाज कर रहा है। बहुत अजीब - और बुरा व्यवहार। क्या आप सीधे 'पीजी' मणि के साथ तैयार कथन का परीक्षण कर सकते हैं यह देखने के लिए कि क्या यह सरणी पैरामीटर को इस तरह से मानता है या यदि यह रेल/ActiveRecord स्तर है? –

+3

@ क्रेग्रिंजर: यह एक रेल/ActiveRecord समस्या होगी 'pg' समस्या नहीं। एआर प्लेसहोल्डर्स को ही संभालता है। –

+2

@ क्रेग्रिंजर: एआर लोग कभी-कभी एसक्यूएल में आश्चर्यजनक बुरा होते हैं, वे इसे उद्देश्य पर कर रहे हैं। यहां "बेटा, मैं निराश हूं" छवि डालें। –

उत्तर

21

ActiveRecord (कम से कम 3.2.1) खाली सरणी NULLs के रूप में व्यवहार करता है। where कॉल में प्लेसहोल्डर sanitize_sql द्वारा प्रबंधित किए जाते हैं। आप थोड़ा के लिए कोड के माध्यम से पता लगाने के हैं, तो आप replace_bind_variables करने आया हूँ:

def replace_bind_variables(statement, values) #:nodoc: 
    raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size) 
    bound = values.dup 
    c = connection 
    statement.gsub('?') { quote_bound_value(bound.shift, c) } 
end 

और फिर quote_bound_value:

def quote_bound_value(value, c = connection) #:nodoc: 
    if value.respond_to?(:map) && !value.acts_like?(:string) 
    if value.respond_to?(:empty?) && value.empty? 
     c.quote(nil) 
    else 
     value.map { |v| c.quote(v) }.join(',') 
    end 
    else 
    c.quote(value) 
    end 
end 

एक खाली सरणी c.quote(nil) को आप प्राप्त करने के लिए सभी चार शर्तों को पूरा हो जाएगा और वह है जहां आपका न्यूल आता है। सभी विशेष तर्क जो c.quote(nil) की ओर जाता है इंगित करता है कि यह जानबूझकर व्यवहार है।

एक रिक्त सूची से IN (या नहीं) कह रही है:

where c in() 

तो शायद एक SQL त्रुटि उत्पन्न चाहिए एआर लोगों को रोकने की कोशिश कर रहे है कि चुपचाप c in (null) में है कि बुरा एसक्यूएल बदल कर। ध्यान दें कि इनमें से कोई भी नहीं:

select ... from t where c in (null); 
select ... from t where c not in (null); 

कभी भी SQL के नल के व्यवहार के कारण किसी भी परिणाम का उत्पादन करना चाहिए। यह एक क्लासिक नौसिखिया गलती है और एआर लोगों को वास्तव में बेहतर पता होना चाहिए।

मैं खुद को एक अपवाद पसंद करूंगा: मुझे बता रहा है कि मैं एक पैर-बुलेट तैनात करने वाला हूं, मुझे बस एक अलग बंदूक सौंपने से ज्यादा मित्रवत होगा।


कार्यकारी सारांश:

  1. यह "खाली सरणी का मतलब शून्य" व्यवहार जानबूझकर है।
  2. आपको कभी भी where('c in (?)', []) या where('c not in (?)', []) का प्रयास नहीं करना चाहिए क्योंकि न तो कथन अधिक समझ में आता है।
  3. रिक्त सरणी की जांच के लिए अपने रूबी कोड को अपडेट करें और जो परिणाम आप उम्मीद करते हैं उन्हें प्राप्त करने के लिए जो भी करने की आवश्यकता है, उसे करें।
+2

हां, सनी चीज * निश्चित रूप से * यहां अपवाद होगी। –

+1

@ क्रेग्रिंजर: मैं, सामान्य रूप से, MySQL को दोष देता हूं :) –

+0

मुझे समझ में आया कि मेरे परीक्षण क्यों विफल हो रहे हैं। :) –

30

रेल 4 में आप User.where.not(id: []) का उपयोग कर सकते हैं जो आपको सही परिणाम देगा।यह पैदा करता है:

SELECT "users".* FROM "users" WHERE (1 = 1) 

दुर्भाग्य User.where('id NOT IN (?)', []) बराबर होना चाहिए, लेकिन यह नहीं है।

SELECT "users".* FROM "users" WHERE (id NOT IN (NULL)) 

संदर्भ::

+0

एक अस्पष्ट रूप से संबंधित नोट पर, User.where (: id => [[], [3]]) आपको अमान्य एसक्यूएल देता है: "उपयोगकर्ताओं को चुनें '। *' उपयोगकर्ता 'से' उपयोगकर्ता 'कहां है। , 3) " –

6
User.where('id NOT IN (?)', ids+[0]) 
यह अभी भी आप गलत परिणाम देता है
+1

कृपया एक स्पष्टीकरण टिप्पणी जोड़ने पर विचार करें *** क्यों *** यह सही जवाब हो सकता है। –

+0

@ डेविडड: यह सुनिश्चित करता है कि सरणी खाली नहीं होगी। यह भी मानता है कि मूल्य = 0 के साथ कोई आईडी नहीं है (रेल में अक्सर यह मामला होता है)। – bragboy

2

उपयोग माणिक की सक्रिय रिकॉर्ड आवरण:

User.where.not(id: []) 

यह आपके लिए खाली सरणी मुद्दे को संभालती है।

1

मुझे नहीं पता कि यह समस्या क्या है, लेकिन मैं खाली रिक्त (धारावाहिक) सरणी विशेषता के साथ सभी रिकॉर्ड खोजने के लिए यहां आया था।

User.where(my_array_attribute: nil) 

या उलटा के लिए:: मैं रेल 5.0 इस तरह के लिए इसे हल

User.where.not(my_array_attribute: nil) 
संबंधित मुद्दे