mã hóa danh sách để sử dụng trong mệnh đề python MySQLDB IN


84

Tôi biết cách ánh xạ một danh sách thành một chuỗi:

foostring = ",".join( map(str, list_of_ids) )

Và tôi biết rằng tôi có thể sử dụng cách sau để đưa chuỗi đó vào mệnh đề IN:

cursor.execute("DELETE FROM foo.bar WHERE baz IN ('%s')" % (foostring))

Những gì tôi cần là hoàn thành điều tương tự AN TOÀN (tránh chèn SQL) bằng MySQLDB. Trong ví dụ trên bởi vì foostring không được truyền như một đối số để thực thi, nó rất dễ bị tấn công. Tôi cũng phải trích dẫn và thoát ra bên ngoài thư viện mysql.

(Có một câu hỏi SO liên quan , nhưng các câu trả lời được liệt kê ở đó hoặc không hoạt động cho MySQLDB hoặc dễ bị chèn SQL.)


Bạn có thể nhận được một số cảm hứng từ một câu hỏi tương tự được thực hiện trong php stackoverflow.com/questions/327274/…
Zoredache


@mluebke Có ý kiến ​​gì về việc chuyển nhiều danh sách trong truy vấn không?
Dipen Dedania

Câu trả lời:


157

Sử dụng list_of_idstrực tiếp:

format_strings = ','.join(['%s'] * len(list_of_ids))
cursor.execute("DELETE FROM foo.bar WHERE baz IN (%s)" % format_strings,
                tuple(list_of_ids))

Bằng cách đó, bạn tránh phải báo giá cho mình và tránh tất cả các loại tiêm sql.

Lưu ý rằng dữ liệu ( list_of_ids) sẽ trực tiếp đến trình điều khiển của mysql, dưới dạng một tham số (không có trong văn bản truy vấn) nên không có nội dung chèn. Bạn có thể để lại bất kỳ ký tự nào bạn muốn trong chuỗi, không cần xóa hoặc trích dẫn ký tự.


2
@heikogerlach: Tôi không trích dẫn% s ... Dòng đầu tiên tạo một chuỗi "% s,% s,% s" ... có cùng kích thước với độ dài list_of_ids.
nosklo

Argh, bạn nói đúng. Cần phải nhìn chăm chỉ hơn. Bằng cách nào đó tôi đã trộn nó lên. Giải pháp tốt, mặc dù.

Điều này cũng sẽ hoạt động trong sqlite? Vì tôi vừa thử nó và nó dường như chỉ ra lỗi cú pháp.
Sohaib

@Sohaib trong sqlite char thay thế ?không hoạt động %svì vậy nó sẽ hoạt động nếu bạn thay đổi dòng đầu tiên thành format_strings = ','.join('?' * len(list_of_ids)).
nosklo

1
@kdas trong trường hợp của bạn, bạn không muốn % format_stringsmột phần để thay đổi khác %splaceholders trong truy vấn của bạn, chỉ IN (%s)giữ chỗ - Các cách để đạt được điều này là để tăng gấp đôi tất cả các %ký tự ngoại trừ một trong những bạn muốn thay thế:query = ("select distinct cln from vcf_commits where branch like %%s and repository like %%s and filename in (%s) and author not like %%s" % format_strings,); cursor.execute(query, (branch, repository) + tuple(fname_list) + (invalid_author,))
nosklo

5

Mặc dù câu hỏi này khá cũ, nhưng tôi nghĩ tốt hơn nên để lại câu trả lời trong trường hợp có người khác đang tìm kiếm thứ tôi muốn

Câu trả lời được chấp nhận sẽ trở nên lộn xộn khi chúng ta có nhiều tham số hoặc nếu chúng ta muốn sử dụng các tham số đã đặt tên

Sau một số thử nghiệm

ids = [5, 3, ...]  # list of ids
cursor.execute('''
SELECT 
...
WHERE
  id IN %(ids)s
  AND created_at > %(start_dt)s
''', {
  'ids': tuple(ids), 'start_dt': '2019-10-31 00:00:00'
})

Đã thử nghiệm với python2.7,pymysql==0.7.11


2
Điều này không hoạt động với python 3 và mysql-connector-python 8.0.21. Lỗi "Không thể chuyển đổi tuple Python thành kiểu MySQL" được trả về.
Rubms

-1

Nếu bạn sử dụng Django 2.0 or 2.1Python 3.6, đây là cách phù hợp:

from django.db import connection
RESULT_COLS = ['col1', 'col2', 'col3']
RESULT_COLS_STR = ', '.join(['a.'+'`'+i+'`' for i in RESULT_COLS])
QUERY_INDEX = RESULT_COLS[0]

TABLE_NAME = 'test'
search_value = ['ab', 'cd', 'ef']  # <-- a list
query = (
    f'SELECT DISTINCT {RESULT_COLS_STR} FROM {TABLE_NAME} a '
    f'WHERE a.`{RESULT_COLS[0]}` IN %s '
    f'ORDER BY a.`{RESULT_COLS[0]}`;'
)  # <- 'SELECT DISTINCT a.`col1`, a.`col2`, a.`col3` FROM test a WHERE a.`col1` IN %s ORDER BY a.`col1`;'
with connection.cursor() as cursor:
    cursor.execute(query, params=[search_value])  # params is a list with a list as its element

ref: https://stackoverflow.com/a/23891759/2803344 https://docs.djangoproject.com/en/2.1/topics/db/sql/#passing-parameters-into-raw


-1

Mặc dù câu hỏi này khá cũ. Tôi đang chia sẻ giải pháp của mình nếu nó có thể giúp được ai đó.

list_to_check = ['A', 'B'] cursor.execute("DELETE FROM foo.bar WHERE baz IN ({})".format(str(list_to_check)[1:-1])

Đã kiểm tra với Python=3.6


Tôi e rằng giải pháp này dễ bị tấn công SQL injection, vì giải pháp được cung cấp list_to_checkkhông phải là SQL-Escape. Đây là lý do tại sao việc chuyển các giá trị dưới dạng tham số executelà thích hợp hơn. Hãy sử dụng giải pháp này thật cẩn thận (nghĩa là các ID đầu vào không được nhận dưới dạng tham số từ bên ngoài ứng dụng của bạn), vì ai đó có thể sử dụng giải pháp này để tấn công hệ thống và truy cập cơ sở dữ liệu của bạn.
Rubms

-2

Một giải pháp đơn giản khác sử dụng khả năng hiểu danh sách:

# creating a new list of strings and convert to tuple
sql_list = tuple([ key.encode("UTF-8") for key in list_of_ids ])

# replace "{}" with "('id1','id2',...'idlast')"
cursor.execute("DELETE FROM foo.bar WHERE baz IN {}".format(sql_list))

-4
list_of_ids = [ 1, 2, 3]
query = "select * from table where x in %s" % str(tuple(list_of_ids))
print query

Điều này có thể hoạt động đối với một số trường hợp sử dụng nếu bạn không muốn quan tâm đến phương thức mà bạn phải truyền các đối số để hoàn thành chuỗi truy vấn và chỉ muốn gọi cursror.execute(query).

Một cách khác có thể là:

"select * from table where x in (%s)" % ', '.join(str(id) for id in list_of_ids)

-7

Rất đơn giản: Chỉ cần sử dụng hình dưới đây

rule_id = ["9", "10"]

sql1 = "CHỌN * TỪ Tham dự_rules_staff WHERE id trong (" + "," .join (map (str, rules_id)) + ")"

"," .join (map (str, rules_id))


Nó thực hiện trích dẫn sql ở đâu và điều này không sử dụng một ký tự thay vì các biến ràng buộc?
eckes

Không cần, nó chỉ đơn giản là hoạt động tốt. Bạn có thể kiểm tra Bởi vì sự hình thành tuple được chuyển đổi trực tiếp dưới dạng chuỗi có dấu ngoặc nhọn đầu tiên ("9", "10"). Mà điều chỉnh hình thành sql. Vì vậy, bạn không cần phải hình khác để làm là sql adjastable
Mizanur Rahman

1
và nếu một rules_idchứa "); DROP TABLES Bobby --?
eckes

Đã nói "nổ tung một danh sách" không ") ... vì vậy trước khi truy vấn mà bạn cần phải xác nhận
Mizanur Rahman

hoặc sử dụng: sql1 = "SELECT * FROM attendance_rules_staff WHERE id trong (" + "" .join (bản đồ (str, rules_id)) + ")"
Mizanur Rahman
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.