elenco python nella query sql come parametro


125

Ho una lista di Python, diciamo l

l = [1,5,8]

Voglio scrivere una query sql per ottenere i dati per tutti gli elementi dell'elenco, ad esempio

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

Come ci riesco?

Risposte:


113

Finora le risposte sono state il modello dei valori in una semplice stringa SQL. Va assolutamente bene per gli interi, ma se volessimo farlo per le stringhe otteniamo il problema dell'escaping.

Ecco una variante che utilizza una query parametrizzata che funzionerebbe per entrambi:

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))sarebbe un po 'più breve mentre ancora leggibile imho
ThiefMaster

7
@ Thiefmaster: sì, dovrebbe essere ([placeholder]*len(l))nel caso generale in quanto il segnaposto potrebbe essere composto da più caratteri.
bobince

1
@bobince nel mio caso ho assegnato una variabile a = 'john' eb = 'snow' e l'ho memorizzata in una tupla con il nome got = ['a, b'] e ho eseguito la stessa query. In questo modo ho ottenuto l'errore di tipo erroy cioè: non tutti gli argomenti convertiti durante la formattazione della stringa. Come dovrei risolverlo
TechJhola

2
Perché vuoi unirti "a mano" piuttosto che lasciare questo lavoro al dbapi? la chiave è usare una tupla piuttosto che una lista ...
Alessandro Dentella

5
Sulla base di questa risposta, ecco una soluzione a riga singola per Python 3.6 cursor.execute(f'SELECT name FROM students WHERE id IN ({','.join('?' for _ in l)})', l) . @bobince, dovresti anche notare nella tua soluzione che l'uso ?è il modo sicuro per evitare l'iniezione SQL. Ci sono molte risposte qui che sono vulnerabili, praticamente tutte quelle che concatena stringhe in Python.
toto_tico

60

Il modo più semplice è girare l'elenco per tupleprimo

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

1
Questa è in realtà la risposta giusta. Non sono sicuro del motivo per cui è stato ignorato. Per prima cosa si imposta la lista l, quindi si usa la tupla, quindi si passa la tupla nella query. molto bene.
MEdwin

11
Come affermato da @renstrm, questo non funziona se l contiene solo un elemento, ti ritroverai con id IN (1,). Che è un errore di sintassi.
Raddoppio

1
@ Boris se non puoi garantire che il tuo input sia sempre un elenco, puoi fare qualcosa di simile a questo stackoverflow.com/questions/38821586/… prima di passare l'argomento alla tua query
Amir Imani

10
Questo, come la maggior parte delle risposte qui diverse da quella accettata, è vulnerabile all'iniezione di SQL e non dovrebbe essere utilizzato.
Bacon Bits

2
@BaconBits Avviso equo, ma per alcune applicazioni in cui SQL injection non è un problema (ad esempio, analisi di un database in cui sono l'unico utente), questo andrà bene.
irene

26

Non complicarlo, la soluzione è semplice.

l = [1,5,8]

l = tuple(l)

params = {'l': l}

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

inserisci qui la descrizione dell'immagine

Spero che questo abbia aiutato !!!


10
Questo non funziona se lcontiene solo un elemento, ti ritroverai con id IN (1,). Che è un errore di sintassi.
renstrm

3
Se l contiene solo 1 elemento assicurati che sia una tupla e che funzionerà. Questa soluzione è secondo me più chiara di quella accettata.
Alessandro Dentella

2
Ma non funzionerà ancora se la tupla è vuota, quindi deve essere effettuato un controllo aggiuntivo prima dell'esecuzione della query.
Никита Конин

2
Questo non funziona per me con sqlite3. Con quale libreria l'hai testato?
Nick Chammas

1
Buona soluzione ma @renstrm e Никита-Конин hanno ragione nel richiedere controlli aggiuntivi per quando la tupla ha un singolo elemento o nessun elemento.
Anish Sana,

23

L'SQL che desideri è

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

Se vuoi costruirlo dal python puoi usare

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

La funzione map trasformerà l'elenco in un elenco di stringhe che possono essere incollate tra loro mediante virgole utilizzando il metodo str.join .

In alternativa:

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

se preferisci le espressioni del generatore alla funzione map.

AGGIORNAMENTO: S. Lott menziona nei commenti che i collegamenti Python SQLite non supportano le sequenze. In tal caso, potresti volerlo

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

Generato da

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

2
:-( L'associazione Python SQLite non supporta le sequenze.
S.Lott

Oh? Non me ne sono reso conto. Poi di nuovo, non mi ero reso conto che stavamo cercando una soluzione di associazione SQLite.
Blair Conrad

Siamo spiacenti, non suggerisco o implica SQLite - è solo triste che i collegamenti per gli elenchi non funzionino lì.
S.Lott

11

stringa. unisci i valori dell'elenco separati da virgole e utilizza l' operatore di formato per formare una stringa di query.

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

(Grazie, Blair-Conrad )


2
Poiché questo approccio non utilizza la sostituzione dei parametri del database, ti espone ad attacchi di SQL injection. Quello %usato qui è solo una semplice formattazione di stringhe Python.
Nick Chammas

8

Mi piace la risposta di 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)

Ma ho notato questo:

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

Può essere sostituito con:

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

Lo trovo più diretto anche se meno intelligente e meno generale. Qui lè necessario avere una lunghezza (cioè fare riferimento a un oggetto che definisce un __len__metodo), che non dovrebbe essere un problema. Ma anche il segnaposto deve essere un singolo carattere. Per supportare un segnaposto multi-carattere, utilizzare:

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

2

Soluzione per la risposta @umounted, perché si è rotto con una tupla di un elemento, poiché (1,) non è SQL valido .:

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

Altra soluzione per stringa sql:

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

5
Questa non è una soluzione migliore a causa dell'iniezione 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)

Questo dovrebbe risolvere il tuo problema.


2

Se stai usando PostgreSQL con la libreria Psycopg2 puoi lasciare che il suo adattamento della tupla esegua tutte le operazioni di escape e interpolazione delle stringhe per te, ad esempio:

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

cioè assicurati di passare il INparametro come file tuple. se è listpossibile utilizzare la = ANYsintassi dell'array :

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

nota che entrambi verranno trasformati nello stesso piano di query, quindi dovresti usare solo quello che è più facile. ad esempio, se la tua lista è in una tupla usa la prima, se sono immagazzinati in una lista usa la seconda.


1

Ad esempio, se desideri la query sql:

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

Che dire:

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

1

una soluzione più semplice:

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)

La :varnotazione sembra più semplice. (Python 3.7)


0

Utilizza la sostituzione dei parametri e si occupa del caso dell'elenco di valori singoli:

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),))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.