daftar python dalam query sql sebagai parameter

125

Saya memiliki daftar python, katakanlah l

l = [1,5,8]

Saya ingin menulis kueri sql untuk mendapatkan data untuk semua elemen daftar, katakanlah

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

Bagaimana saya melakukannya?

Mohit Ranka
sumber

Jawaban:

113

Jawaban sejauh ini telah membuat template nilai menjadi string SQL biasa. Itu baik-baik saja untuk integer, tetapi jika kita ingin melakukannya untuk string kita mendapatkan masalah escaping.

Berikut adalah varian yang menggunakan kueri berparameter yang akan berfungsi untuk keduanya:

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)
bobince
sumber
11
','.join(placeholder * len(l))akan menjadi sedikit lebih pendek sementara masih dapat dibaca imho
ThiefMaster
7
@ Pencuri: ya, harus ([placeholder]*len(l))dalam kasus umum karena tempat penampung dapat terdiri dari banyak karakter.
bobince
1
@bobince dalam kasus saya, saya telah menetapkan variabel a = 'john' dan b = 'snow' dan menyimpannya dalam tupel dengan nama got = ['a, b'] dan melakukan kueri yang sama. Melakukan ini saya mendapat erroy yaitu Type Error: tidak semua argumen diubah selama pemformatan string. Bagaimana saya bisa memecahkannya
TechJhola
2
Mengapa Anda ingin bergabung "dengan tangan" daripada menyerahkan pekerjaan ini kepada dbapi? kuncinya adalah menggunakan tupel daripada daftar ...
Alessandro Dentella
5
Berdasarkan jawaban ini, berikut adalah solusi baris tunggal untuk Python 3.6 cursor.execute(f'SELECT name FROM students WHERE id IN ({','.join('?' for _ in l)})', l) . @bobince, Anda juga harus berkomentar dalam solusi Anda bahwa menggunakan ?adalah cara yang aman untuk menghindari injeksi SQL. Ada banyak jawaban di sini yang rentan, pada dasarnya semua yang menggabungkan string dengan python.
toto_tico
60

Cara termudah adalah dengan mengubah daftar menjadi yang tuplepertama

t = tuple(l)
query = "select name from studens where id IN {}".format(t)
Amir Imani
sumber
1
Ini sebenarnya jawaban yang benar. Saya tidak yakin mengapa itu diabaikan. Pertama Anda menyiapkan daftar l, lalu gunakan tupel, lalu teruskan tupel ke dalam kueri. sudah selesai dilakukan dengan baik.
MEdwin
11
Seperti yang dinyatakan oleh @renstrm, ini tidak berfungsi jika l hanya berisi satu elemen, Anda akan berakhir dengan id IN (1,). Yang merupakan kesalahan sintaks.
Doubledown
1
@ Boris jika Anda tidak dapat menjamin bahwa masukan Anda selalu berupa daftar, Anda dapat melakukan sesuatu yang mirip dengan yang satu ini stackoverflow.com/questions/38821586/… sebelum meneruskan argumen ke kueri Anda
Amir Imani
10
Ini, seperti kebanyakan jawaban di sini selain yang diterima, rentan terhadap injeksi SQL dan tidak boleh digunakan.
Bacon Bits
2
@BaconBits Peringatan yang adil, tetapi untuk beberapa aplikasi di mana injeksi SQL tidak menjadi masalah (misalnya, analisis database di mana saya satu-satunya pengguna), ini akan dilakukan.
irene
26

Jangan mempersulit, Solusi untuk ini sederhana.

l = [1,5,8]

l = tuple(l)

params = {'l': l}

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

masukkan deskripsi gambar di sini

Saya harap ini membantu !!!

allsyed
sumber
10
Ini tidak berfungsi jika lhanya berisi satu elemen, Anda akan berakhir dengan id IN (1,). Yang merupakan kesalahan sintaks.
renstrm
3
Jika l hanya berisi 1 elemen pastikan itu tuple dan AKAN bekerja. Solusi ini lebih jelas menurut saya daripada yang diterima.
Alessandro Dentella
2
Tetapi masih tidak akan berfungsi jika tuple kosong, jadi harus ada pemeriksaan tambahan sebelum eksekusi kueri.
Никита Конин
2
Ini tidak berhasil untuk saya dengan sqlite3. Perpustakaan apa yang Anda uji ini?
Nick Chammas
1
Solusi bagus tetapi @renstrm dan Никита-Конин benar tentang mewajibkan pemeriksaan tambahan ketika tupel memiliki satu elemen atau tanpa elemen.
Anish Sana
23

SQL yang Anda inginkan adalah

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

Jika Anda ingin membuat ini dari python, Anda dapat menggunakan

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

Fungsi peta akan mengubah daftar menjadi daftar string yang dapat direkatkan dengan koma menggunakan metode str.join .

Kalau tidak:

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

jika Anda lebih suka ekspresi generator daripada fungsi peta.

UPDATE: S. Lott menyebutkan di komentar bahwa binding Python SQLite tidak mendukung urutan. Dalam hal ini, Anda mungkin ingin

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

Dihasilkan oleh

sql_query = 'select name from studens where ' + ' or '.join(('id = ' + str(n) for n in l))
Blair Conrad
sumber
2
:-( Pengikatan Python SQLite tidak mendukung urutan.
S.Lott
Oh? Saya tidak menyadarinya. Kemudian lagi, saya tidak menyadari kami akan mencari solusi pengikatan SQLite.
Blair Conrad
Maaf, tidak menyarankan atau menyiratkan SQLite - sayangnya binding untuk list tidak berfungsi di sana.
S. Lott
11

string. Bergabunglah dengan nilai daftar yang dipisahkan dengan koma, dan gunakan operator format untuk membentuk string kueri.

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

(Terima kasih, blair-conrad )

gimel
sumber
2
Karena pendekatan ini tidak menggunakan substitusi parameter database, ini membuat Anda terkena serangan injeksi SQL. Yang %digunakan di sini hanyalah pemformatan string Python biasa.
Nick Chammas
8

Saya suka jawaban 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)

Tapi saya perhatikan ini:

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

Bisa diganti dengan:

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

Saya menemukan ini lebih langsung jika kurang pintar dan kurang umum. Di sini ldiperlukan untuk memiliki panjang (yaitu merujuk ke objek yang mendefinisikan __len__metode), yang seharusnya tidak menjadi masalah. Tapi placeholder juga harus satu karakter. Untuk mendukung penggunaan placeholder multi-karakter:

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

Solusi untuk jawaban @umounted, karena itu putus dengan tupel satu elemen, karena (1,) bukan SQL yang valid .:

>>> 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,)]

Solusi lain untuk string sql:

cursor.execute("select id from test where id in (%s)" % ('"'+'", "'.join(l)+'"'))
Ximix
sumber
5
Ini bukan solusi yang lebih baik karena injeksi 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)

Ini seharusnya menyelesaikan masalah Anda.

Rishabh Jain
sumber
2

Jika Anda menggunakan PostgreSQL dengan pustaka Psycopg2, Anda dapat membiarkan adaptasi tupelnya melakukan semua pelolosan dan interpolasi string untuk Anda, misalnya:

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

yaitu, pastikan Anda meneruskan INparameter sebagai a tuple. jika itu adalah listAnda dapat menggunakan = ANYsintaks array :

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

perhatikan bahwa keduanya akan diubah menjadi rencana kueri yang sama sehingga Anda sebaiknya menggunakan mana saja yang lebih mudah. misalnya jika daftar Anda datang dalam bentuk tupel, gunakan yang pertama, jika mereka disimpan dalam daftar, gunakan yang terakhir.

Sam Mason
sumber
1

Misalnya, jika Anda menginginkan kueri sql:

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

Bagaimana dengan:

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

solusi yang lebih sederhana:

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

query = f"""SELECT * FROM table WHERE IN {str(lst)[1:-1}"""
Omar Omeiri
sumber
1
l = [1] # or [1,2,3]

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

The :varnotasi tampaknya sederhana. (Python 3.7)

pengguna13476428
sumber
0

Ini menggunakan substitusi parameter dan menangani kasus daftar nilai tunggal:

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),))
Roland Mechler
sumber