2009-08-21 38 views
31

Tôi đang cố sử dụng thay thế tham số với SQLite within Python cho mệnh đề IN. Đây là một hoạt động ví dụ hoàn chỉnh thể hiện:Thay thế tham số cho mệnh đề "IN" SQLite

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 

Nó in ra:

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

Như các tài liệu nêu rằng "[y] ou không nên lắp ráp truy vấn của bạn sử dụng các hoạt động chuỗi Python vì làm như vậy không an toàn, nó làm cho chương trình của bạn dễ bị tấn công SQL injection, "Tôi hy vọng sẽ sử dụng thay thế tham số.

Khi tôi cố gắng:

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

tôi nhận được một tập kết quả có sản phẩm nào, và khi tôi thử:

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

tôi nhận được:

InterfaceError: Error binding parameter 0 - probably unsupported type.

Trong khi tôi hy vọng rằng bất kỳ câu trả lời vấn đề đơn giản hóa này sẽ hoạt động, tôi muốn chỉ ra rằng truy vấn thực tế mà tôi muốn thực hiện nằm trong truy vấn con lồng nhau gấp đôi. Để wit:

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

Cảm ơn tất cả các câu trả lời. Nó đã có rất nhiều ý nghĩa khi tôi cuối cùng đã thấy rằng tôi chỉ cần một dấu hỏi cho mỗi tham số tôi thay thế. –

Trả lời

47

Bạn làm cần số bên phải của ? s, nhưng điều đó không gây nguy hiểm sql injection:

>>> 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 cho giải pháp "tốt nhất" để tạo chuỗi danh sách trình giữ chỗ :-) –

+0

Thay vào đó, có cách dễ dàng để sử dụng thông số được đặt tên này không? Một cái gì đó như ': id1: id2: id3' vv Tôi đang sử dụng điều này trong ngữ cảnh của một truy vấn lớn hơn với một vài tham số có tên khác. – User

+2

Tôi sẽ đến đây vài năm sau đó, nhưng tôi cũng cần các thông số có tên. Tôi chỉ làm điều này: 'truy vấn =" SELECT * TỪ my_table WHERE my_param =: my_param VÀ id IN ({}) ". Định dạng (',' .join (': {}'. Định dạng (i) cho i in phạm vi (len (mong muốn_ids)))); params = {'my_param': 'foo'}; params.update ({str (i): id cho i, id trong liệt kê (mong muốn)}); result = cursor.execute (truy vấn, params) ' Mô-đun sqlite3 hoàn toàn hài lòng với những thứ như': 0', ': 1',': 2' như các tham số thay thế chuỗi. (Stack overflow thực sự giết các định dạng mã trong các ý kiến; xin lỗi đó là rất khó đọc.) – geekofalltrades

10

Cập nhật: Công việc này:

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 

Vấn đề là bạn cần phải có một? cho mỗi phần tử trong danh sách đầu vào.

Câu lệnh ("?," * len(desired_ids))[:-1] tạo chuỗi lặp "", ", sau đó cắt bỏ dấu phẩy cuối cùng. để có một dấu chấm hỏi cho mỗi phần tử trong các_kỳ_kỳ mong muốn.

+0

Đó là một lời giải thích tuyệt vời. Cảm ơn bạn. –

2

tôi luôn luôn kết thúc lên làm một cái gì đó như thế này:

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

Không có nguy cơ tiêm vì bạn không đặt chuỗi từ mong muốn vào truy vấn trực tiếp.

+0

Các giá trị tôi sẽ sử dụng trong mệnh đề IN thực sự đến từ một tập tin xuất khẩu từ hệ thống khác. Tôi hy vọng rằng nguy cơ tiêm là rất nhỏ, nhưng bạn không bao giờ biết khi nào Bobby Tables sẽ xuất hiện. –

+0

Nguy cơ tiêm là 0 bởi vì điều duy nhất bạn lập trình đưa vào truy vấn của bạn là một loạt các dấu hỏi. Tất cả kẻ tấn công giả thuyết có thể làm là kiểm soát số lượng dấu hỏi - đó không phải là một vectơ tấn công. Dữ liệu được cung cấp bên ngoài thực tế đang trải qua? cơ chế truyền tham số như bình thường. –

+0

Tôi hiểu. Vâng, bạn nói đúng. –

0

Trong trường hợp sqlite có vấn đề với độ dài yêu cầu sql, số lượng dấu hỏi không xác định có thể là một số cách để khai thác mọi thứ.

18

Theo http://www.sqlite.org/limits.html (mục 9), SQLite không thể (theo mặc định) xử lý hơn 999 tham số cho truy vấn, do đó các giải pháp ở đây (tạo danh sách yêu cầu của trình giữ chỗ) sẽ thất bại nếu bạn có hàng nghìn mục bạn đang tìm kiếm IN. Nếu đó là trường hợp, bạn sẽ cần phải phá vỡ danh sách sau đó vòng qua các phần của nó và tham gia các kết quả cho mình.

Nếu bạn không cần hàng nghìn mục trong mệnh đề IN, thì giải pháp của Alex là cách thực hiện (và có vẻ như cách Django thực hiện).

+0

Điều cần biết. Cảm ơn. Tôi thậm chí có thể phải revist mã của tôi. –

Các vấn đề liên quan