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?
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:
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)
([placeholder]*len(l))
nel caso generale in quanto il segnaposto potrebbe essere composto da più caratteri.
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.
Il modo più semplice è girare l'elenco per tuple
primo
t = tuple(l)
query = "select name from studens where id IN {}".format(t)
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)
Spero che questo abbia aiutato !!!
l
contiene solo un elemento, ti ritroverai con id IN (1,)
. Che è un errore di sintassi.
sqlite3
. Con quale libreria l'hai testato?
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))
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 )
%
usato qui è solo una semplice formattazione di stringhe Python.
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))
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)+'"'))
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.
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 IN
parametro come file tuple
. se è list
possibile utilizzare la = ANY
sintassi 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.
una soluzione più semplice:
lst = [1,2,3,a,b,c]
query = f"""SELECT * FROM table WHERE IN {str(lst)[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 :var
notazione sembra più semplice. (Python 3.7)
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),))
','.join(placeholder * len(l))
sarebbe un po 'più breve mentre ancora leggibile imho