danh sách python trong truy vấn sql dưới dạng tham số


125

Tôi có một danh sách trăn, nói l

l = [1,5,8]

Tôi muốn viết một truy vấn sql để lấy dữ liệu cho tất cả các phần tử của danh sách, giả sử

select name from students where id = |IN THE LIST l|

Làm cách nào để thực hiện được điều này?

Câu trả lời:


112

Các câu trả lời cho đến nay đã được tạo mẫu các giá trị thành một chuỗi SQL thuần túy. Điều đó hoàn toàn tốt cho các số nguyên, nhưng nếu chúng tôi muốn làm điều đó cho các chuỗi, chúng tôi gặp sự cố thoát.

Đây là một biến thể sử dụng truy vấn được tham số hóa sẽ hoạt động cho cả hai:

placeholder= '?' # For SQLite. See DBAPI paramstyle.
placeholders= ', '.join(placeholder for unused in l)
query= 'SELECT name FROM students WHERE id IN (%s)' % placeholders
cursor.execute(query, l)

11
','.join(placeholder * len(l))sẽ ngắn hơn một chút trong khi vẫn có thể đọc được
imho

7
@Thiefmaster: vâng, sẽ phải ([placeholder]*len(l))ở trong trường hợp chung vì trình giữ chỗ có thể gồm nhiều ký tự.
bobince

1
@bobince trong trường hợp của tôi, tôi đã gán một biến a = 'john' và b = 'snow' và lưu trữ nó trong một bộ tuple với tên got = ['a, b'] và đã thực hiện cùng một truy vấn. Làm điều này tôi nhận được lỗi tức là Lỗi loại: không phải tất cả các đối số được chuyển đổi trong quá trình định dạng chuỗi. Làm sao tôi phải solbe nó
TechJhola

2
Tại sao bạn muốn tham gia "bằng tay" hơn là giao việc này cho dbapi? điều quan trọng là sử dụng một tuple thay vì một danh sách ...
Alessandro Dentella

5
Dựa trên câu trả lời này, đây là một giải pháp dòng đơn cho Python 3.6 cursor.execute(f'SELECT name FROM students WHERE id IN ({','.join('?' for _ in l)})', l) . @bobince, bạn cũng nên lưu ý trong giải pháp của mình rằng sử dụng ?là cách an toàn để tránh tiêm SQL. Có rất nhiều câu trả lời ở đây dễ bị tấn công, về cơ bản là bất kỳ câu trả lời nào nối các chuỗi trong python.
toto_tico

59

Cách dễ nhất là chuyển danh sách thành tupleđầu tiên

t = tuple(l)
query = "select name from studens where id IN {}".format(t)

1
Đây thực sự là câu trả lời đúng. Tôi không chắc tại sao nó bị bỏ qua. Đầu tiên bạn thiết lập danh sách l, sau đó sử dụng tuple, sau đó chuyển tuple vào truy vấn. làm tốt.
MEdwin

11
Như đã nêu bởi @renstrm, điều này không hoạt động nếu tôi chỉ chứa một phần tử, bạn sẽ kết thúc với id IN (1,). Đó là một lỗi cú pháp.
Doubledown

1
@Boris nếu bạn không thể đảm bảo rằng đầu vào của bạn luôn luôn là một danh sách, bạn có thể làm điều gì đó tương tự như một liner này stackoverflow.com/questions/38821586/... trước khi đi qua các đối số cho truy vấn của bạn
Amir Imani

10
Điều này, giống như hầu hết các câu trả lời ở đây khác với câu được chấp nhận, dễ bị chèn SQL và không nên được sử dụng.
Bacon Bits

2
@BaconBits Cảnh báo công bằng, nhưng đối với một số ứng dụng mà việc chèn SQL không phải là một vấn đề (ví dụ: phân tích cơ sở dữ liệu mà tôi là người dùng duy nhất), điều này sẽ thực hiện được.
irene

26

Đừng làm phức tạp nó, Giải pháp cho điều này rất đơn giản.

l = [1,5,8]

l = tuple(l)

params = {'l': l}

cursor.execute('SELECT * FROM table where id in %(l)s',params)

nhập mô tả hình ảnh ở đây

Tôi hy vọng điều này đã giúp !!!


10
Điều này không hoạt động nếu lchỉ chứa một phần tử, bạn sẽ kết thúc với id IN (1,). Đó là một lỗi cú pháp.
renstrm vào

3
Nếu l chỉ chứa 1 phần tử, hãy chắc chắn rằng đó là một bộ và nó SẼ hoạt động. Theo tôi giải pháp này rõ ràng hơn giải pháp được chấp nhận.
Alessandro Dentella

2
Nhưng nó vẫn sẽ không hoạt động nếu tuple trống, vì vậy phải kiểm tra thêm trước khi thực hiện truy vấn.
Никита Конин

2
Điều này không làm việc cho tôi với sqlite3. Thư viện nào bạn đã kiểm tra điều này chống lại?
Nick Chammas

1
Giải pháp tốt nhưng @renstrm và Никита-Конин đã đúng về việc yêu cầu kiểm tra bổ sung khi bộ tuple có một phần tử duy nhất hoặc không có phần tử nào.
Anish Sana

23

SQL bạn muốn là

select name from studens where id in (1, 5, 8)

Nếu bạn muốn xây dựng điều này từ python, bạn có thể sử dụng

l = [1, 5, 8]
sql_query = 'select name from studens where id in (' + ','.join(map(str, l)) + ')'

Hàm bản đồ sẽ biến đổi danh sách thành một danh sách các chuỗi có thể được gắn với nhau bằng dấu phẩy bằng cách sử dụng phương thức str.join .

Ngoài ra:

l = [1, 5, 8]
sql_query = 'select name from studens where id in (' + ','.join((str(n) for n in l)) + ')'

nếu bạn thích biểu thức trình tạo hơn hàm bản đồ.

CẬP NHẬT: S. Lott đề cập trong các nhận xét rằng các liên kết SQLite trong Python không hỗ trợ trình tự. Trong trường hợp đó, bạn có thể muốn

select name from studens where id = 1 or id = 5 or id = 8

Tạo bởi

sql_query = 'select name from studens where ' + ' or '.join(('id = ' + str(n) for n in l))

2
:-( Liên kết SQLite trong Python không hỗ trợ trình tự.
S.Lott

Oh? Tôi không nhận ra. Sau đó, một lần nữa, tôi không nhận ra rằng chúng tôi đang tìm kiếm một giải pháp liên kết SQLite.
Blair Conrad

Xin lỗi, không đề xuất hoặc ngụ ý SQLite - chỉ buồn rằng các liên kết cho danh sách không hoạt động ở đó.
S.Lott

11

string.join các giá trị danh sách được phân tách bằng dấu phẩy và sử dụng toán tử định dạng để tạo thành một chuỗi truy vấn.

myquery = "select name from studens where id in (%s)" % ",".join(map(str,mylist))

(Cảm ơn, blair-conrad )


2
Vì cách tiếp cận này không sử dụng thay thế tham số cơ sở dữ liệu, nó khiến bạn bị tấn công SQL injection. Việc %được sử dụng ở đây chỉ là định dạng chuỗi Python đơn giản.
Nick Chammas

8

Tôi thích câu trả lời của bobince:

placeholder= '?' # For SQLite. See DBAPI paramstyle.
placeholders= ', '.join(placeholder for unused in l)
query= 'SELECT name FROM students WHERE id IN (%s)' % placeholders
cursor.execute(query, l)

Nhưng tôi nhận thấy điều này:

placeholders= ', '.join(placeholder for unused in l)

Có thể được thay thế bằng:

placeholders= ', '.join(placeholder*len(l))

Tôi thấy điều này trực tiếp hơn nếu ít thông minh hơn và ít chung chung hơn. Ở đây lbắt buộc phải có độ dài (tức là tham chiếu đến một đối tượng xác định __len__phương thức), điều này không thành vấn đề. Nhưng trình giữ chỗ cũng phải là một ký tự duy nhất. Để hỗ trợ việc sử dụng trình giữ chỗ nhiều ký tự:

placeholders= ', '.join([placeholder]*len(l))

2

Giải pháp cho câu trả lời @umounted, vì điều đó đã phá vỡ với bộ tuple một phần tử, vì (1,) không phải là SQL hợp lệ.:

>>> random_ids = [1234,123,54,56,57,58,78,91]
>>> cursor.execute("create table test (id)")
>>> for item in random_ids:
    cursor.execute("insert into test values (%d)" % item)
>>> sublist = [56,57,58]
>>> cursor.execute("select id from test where id in %s" % str(tuple(sublist)).replace(',)',')'))
>>> a = cursor.fetchall()
>>> a
[(56,), (57,), (58,)]

Giải pháp khác cho chuỗi sql:

cursor.execute("select id from test where id in (%s)" % ('"'+'", "'.join(l)+'"'))

5
Đây không phải là giải pháp tốt hơn vì tiêm sql.
Anurag

2
placeholders= ', '.join("'{"+str(i)+"}'" for i in range(len(l)))
query="select name from students where id (%s)"%placeholders
query=query.format(*l)
cursor.execute(query)

Điều này sẽ giải quyết vấn đề của bạn.


2

Nếu bạn đang sử dụng PostgreSQL với thư viện Psycopg2, bạn có thể cho phép bộ điều chỉnh bộ của nó thực hiện tất cả việc thoát và nội suy chuỗi cho bạn, ví dụ:

ids = [1,2,3]
cur.execute(
  "SELECT * FROM foo WHERE id IN %s",
  [tuple(ids)])

tức là chỉ cần đảm bảo rằng bạn đang truyền INtham số dưới dạng a tuple. nếu đó là một, listbạn có thể sử dụng = ANYcú pháp mảng :

cur.execute(
  "SELECT * FROM foo WHERE id = ANY (%s)",
  [list(ids)])

lưu ý rằng cả hai sẽ được chuyển thành cùng một kế hoạch truy vấn, vì vậy bạn chỉ nên sử dụng cái nào dễ dàng hơn. ví dụ: nếu danh sách của bạn có trong một bộ, hãy sử dụng cái trước, nếu chúng được lưu trữ trong một danh sách, hãy sử dụng cái sau.


1

Ví dụ: nếu bạn muốn truy vấn sql:

select name from studens where id in (1, 5, 8)

Thế còn:

my_list = [1, 5, 8]
cur.execute("select name from studens where id in %s" % repr(my_list).replace('[','(').replace(']',')') )

1

một giải pháp đơn giản hơn:

lst = [1,2,3,a,b,c]

query = f"""SELECT * FROM table WHERE IN {str(lst)[1:-1}"""

1
l = [1] # or [1,2,3]

query = "SELECT * FROM table WHERE id IN :l"
params = {'l' : tuple(l)}
cursor.execute(query, params)

Các :varký hiệu dường như đơn giản hơn. (Python 3.7)


0

Điều này sử dụng thay thế tham số và xử lý trường hợp danh sách giá trị đơn:

l = [1,5,8]

get_operator = lambda x: '=' if len(x) == 1 else 'IN'
get_value = lambda x: int(x[0]) if len(x) == 1 else x

query = 'SELECT * FROM table where id ' + get_operator(l) + ' %s'

cursor.execute(query, (get_value(l),))
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.