2009-08-21 9 views
31

के लिए पैरामीटर प्रतिस्थापन मैं एक IN खंड के लिए SQLite within Python के साथ पैरामीटर प्रतिस्थापन का उपयोग करने का प्रयास कर रहा हूं।SQLite "IN" खंड

import sqlite3 

c = sqlite3.connect(":memory:") 
c.execute('CREATE TABLE distro (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)') 

for name in 'Ubuntu Fedora Puppy DSL SuSE'.split(): 
    c.execute('INSERT INTO distro (name) VALUES (?)', [ name ]) 

desired_ids = ["1", "2", "5", "47"] 
result_set = c.execute('SELECT * FROM distro WHERE id IN (%s)' % (", ".join(desired_ids)),()) 
for result in result_set: 
    print result 

यह पता प्रिंट:

(1, u'Ubuntu') (2, u'Fedora') (5, u'SuSE')

डॉक्स राज्य के रूप में है कि "[y] कहां अजगर की स्ट्रिंग आपरेशन का उपयोग करते हुए, क्योंकि ऐसा करने आपकी क्वेरी को इकट्ठा नहीं करना चाहिए यहां एक संपूर्ण चल उदाहरण है कि यह दर्शाता है है असुरक्षित है; यह आपके प्रोग्राम को एसक्यूएल इंजेक्शन हमले के लिए कमजोर बनाता है, "मैं पैरामीटर प्रतिस्थापन का उपयोग करने की उम्मीद कर रहा हूं।

जब मैं कोशिश:

result_set = c.execute('SELECT * FROM distro WHERE id IN (?)', [ (", ".join(desired_ids)) ]) 

मैं एक खाली परिणाम सेट मिलता है, और जब मैं कोशिश:

result_set = c.execute('SELECT * FROM distro WHERE id IN (?)', [ desired_ids ]) 

मैं:

InterfaceError: Error binding parameter 0 - probably unsupported type.

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

UPDATE dir_x_user SET user_revision = user_attempted_revision 
WHERE user_id IN 
    (SELECT user_id FROM 
     (SELECT user_id, MAX(revision) FROM users WHERE obfuscated_name IN 
      ("Argl883", "Manf496", "Mook657") GROUP BY user_id 
     ) 
    ) 
+0

सभी उत्तरों के लिए धन्यवाद। जब मैंने अंततः देखा कि मुझे प्रतिस्थापन के हर पैरामीटर के लिए एक प्रश्न चिह्न की आवश्यकता है तो यह बहुत समझ में आया। –

उत्तर

47

आप ? रों की सही संख्या की आवश्यकता है, लेकिन यह एक एसक्यूएल इंजेक्शन खतरा पैदा नहीं करता है:

>>> result_set = c.execute('SELECT * FROM distro WHERE id IN (%s)' % 
          ','.join('?'*len(desired_ids)), desired_ids) 
>>> print result_set.fetchall() 
[(1, u'Ubuntu'), (2, u'Fedora'), (5, u'SuSE')] 
+0

+1 :-) –

+0

क्या इस उपयोग को पैरामीटर नामित करने का कोई आसान तरीका है? कुछ: 'id1: id2: id3' आदि जैसे कुछ मैं कुछ अन्य नामित पैरामीटर के साथ एक बड़ी क्वेरी के संदर्भ में इसका उपयोग कर रहा हूं। – User

+2

मैं इस कई सालों बाद आ रहा हूं, लेकिन मुझे नामित पैरामीटर भी चाहिए। मैंने अभी यह किया है: 'query =" चुनें * my_table से जहां my_param =: my_param और id in ({}) "। प्रारूप (',' .join (': {}'। प्रारूप (i) में मैं रेंज (लेन (वांछित_आईडीएस))); पैराम्स = {'my_param': 'foo'}; params.update ({str (i): आईडी के लिए आईडी, आईडी में गणना (वांछित_आईडीएस}}); परिणाम = cursor.execute (क्वेरी, पैराम्स) ' sqlite3 मॉड्यूल स्ट्रिंग प्रतिस्थापन पैरामीटर के रूप में': 0', ': 1',': 2' जैसी चीज़ों से पूरी तरह से खुश है। (स्टैक ओवरफ़्लो वास्तव में टिप्पणियों में कोड स्वरूपण की हत्या करता है; क्षमा करें कि पढ़ने के लिए बहुत मुश्किल है।) – geekofalltrades

10

अपडेट: इस काम करता है:

import sqlite3 

c = sqlite3.connect(":memory:") 
c.execute('CREATE TABLE distro (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)') 

for name in 'Ubuntu Fedora Puppy DSL SuSE'.split(): 
    c.execute('INSERT INTO distro (name) VALUES (?)', (name,)) 

desired_ids = ["1", "2", "5", "47"] 
result_set = c.execute('SELECT * FROM distro WHERE id IN (%s)' % ("?," * len(desired_ids))[:-1], desired_ids) 
for result in result_set: 
    print result 

मुद्दा यह है कि यदि आपके पास यह करने की जरूरत थी? इनपुट सूची में प्रत्येक तत्व के लिए।

कथन ("?," * len(desired_ids))[:-1] "?," की दोहराने वाली स्ट्रिंग बनाता है, फिर अंतिम कॉमा को काट देता है। ताकि वांछित_आईड्स में प्रत्येक तत्व के लिए एक प्रश्न चिह्न हो।

+0

यह एक महान स्पष्टीकरण था। धन्यवाद।प्लेसहोल्डर-सूची स्ट्रिंग जेनरेट करने के लिए "सर्वोत्तम" समाधान के लिए –

2

मैं हमेशा कुछ इस तरह कर रही अंत:

query = 'SELECT * FROM distro WHERE id IN (%s)' % ','.join('?' for i in desired_ids) 
c.execute(query, desired_ids) 

कोई है इंजेक्शन जोखिम क्योंकि आप वांछित_आईड्स से सीधे क्वेरी में स्ट्रिंग नहीं डाल रहे हैं।

+0

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

+0

इंजेक्शन का जोखिम 0 है क्योंकि एकमात्र चीज जिसे आप प्रोग्रामिंग रूप से अपनी क्वेरी में डाल रहे हैं, प्रश्न चिह्नों का एक गुच्छा है। सभी एक काल्पनिक हमलावर प्रश्न चिह्नों की संख्या को नियंत्रित कर सकता है - यह एक हमला वेक्टर नहीं है। वास्तविक बाहरी आपूर्ति किए गए डेटा के माध्यम से जा रहा है? सामान्य रूप से पैरामीटर-गुजरने तंत्र। –

+0

मैं देखता हूं। हाँ आप सही है। –

0

यदि एसक्लाइट एसक्यूएल अनुरोध की लंबाई के साथ समस्या है, तो अनिश्चित संख्या में प्रश्न चिह्न बीक चीजों के लिए किसी तरह का तरीका हो सकता है।

18

http://www.sqlite.org/limits.html (आइटम 9) के अनुसार, SQLite (डिफ़ॉल्ट रूप से) क्वेरी के 999 पैरामीटर से अधिक संभाल नहीं सकता है, इसलिए यहां समाधान (प्लेसहोल्डर की आवश्यक सूची उत्पन्न करना) विफल हो जाएगा यदि आपके पास हजारों आइटम हैं आप IN देख रहे हैं। यदि ऐसा है, तो आपको सूची को तोड़ने की आवश्यकता होगी, इसके बाद इसके हिस्सों पर लूप करें और परिणामों को स्वयं शामिल करें।

यदि आपको अपने IN खंड में हजारों आइटमों की आवश्यकता नहीं है, तो एलेक्स का समाधान ऐसा करने का तरीका है (और ऐसा लगता है कि Django यह कैसे करता है)।

+0

जानना अच्छा है। धन्यवाद। मुझे अपने कोड को फिर से संशोधित करना पड़ सकता है। –