lista de python en consulta sql como parámetro

124

Tengo una lista de python, digamos l

l = [1,5,8]

Quiero escribir una consulta SQL para obtener los datos de todos los elementos de la lista, digamos

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

¿Cómo logro esto?

Mohit Ranka
fuente

Respuestas:

111

Hasta ahora, las respuestas han estado creando los valores en una cadena SQL simple. Eso está absolutamente bien para los enteros, pero si quisiéramos hacerlo por cadenas obtenemos el problema de escape.

Aquí hay una variante que usa una consulta parametrizada que funcionaría para ambos:

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
fuente
11
','.join(placeholder * len(l))sería un poco más corto mientras aún legible en mi humilde opinión
ThiefMaster
77
@ Thhiefmaster: sí, tendría que estar ([placeholder]*len(l))en el caso general ya que el marcador de posición puede tener varios caracteres.
bobince
1
@bobince en mi caso, le asigné una variable a = 'john' yb = 'snow' y la almacené en una tupla con el nombre got = ['a, b'] y realicé la misma consulta. Al hacer esto obtuve el error, es decir, Error de tipo: no todos los argumentos se convirtieron durante el formateo de cadenas. ¿Cómo se supone que voy a resolverlo?
TechJhola
2
¿Por qué quieres unirte "a mano" en lugar de dejar este trabajo al dbapi? la clave es usar una tupla en lugar de una lista ...
Alessandro Dentella
55
Basado en esta respuesta, aquí hay una solución de una sola línea para Python 3.6 cursor.execute(f'SELECT name FROM students WHERE id IN ({','.join('?' for _ in l)})', l) . @bobince, también debe comentar en su solución que usar ?es la forma segura de evitar la inyección de SQL. Aquí hay muchas respuestas que son vulnerables, básicamente cualquiera que concatene cadenas en python.
toto_tico
59

La forma más fácil es convertir tupleprimero la lista

t = tuple(l)
query = "select name from studens where id IN {}".format(t)
Amir Imani
fuente
1
Esta es realmente la respuesta correcta. No estoy seguro de por qué fue ignorado. Primero configura la lista l, luego usa tupla, luego pasa la tupla a la consulta. bien hecho.
MEdwin
11
Como lo indicó @renstrm, esto no funciona si solo contiene un elemento, terminarás con id IN (1,). Que es un error de sintaxis.
Doubledown
1
@ Boris si no puede garantizar que su entrada sea siempre una lista, puede hacer algo similar a este liner stackoverflow.com/questions/38821586/... antes de pasar el argumento a su consulta
Amir Imani
10
Esto, como la mayoría de las respuestas aquí, excepto la aceptada, es vulnerable a la inyección de SQL y no debe usarse.
Bacon Bits
2
@BaconBits Advertencia justa, pero para algunas aplicaciones donde la inyección de SQL no es un problema (por ejemplo, análisis de una base de datos donde soy el único usuario), esto servirá.
Irene
26

No lo complique, la solución para esto es simple.

l = [1,5,8]

l = tuple(l)

params = {'l': l}

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

ingrese la descripción de la imagen aquí

Espero que esto haya ayudado !!!

allsyed
fuente
10
Esto no funciona si lsolo contiene un elemento, terminarás con id IN (1,). Que es un error de sintaxis.
Renstrm
3
Si solo contiene 1 elemento, asegúrese de que sea una tupla y que FUNCIONARÁ. Esta solución es más clara en mi opinión que la aceptada.
Alessandro Dentella
2
Pero todavía no funcionará si la tupla está vacía, por lo que debe haber una verificación adicional antes de la ejecución de la consulta.
Никита Конин
2
Esto no funciona para mí sqlite3. ¿Contra qué biblioteca probaste esto?
Nick Chammas
1
Buena solución, pero @renstrm y Никита-Конин tienen razón al solicitar controles adicionales para cuando la tupla tiene un solo elemento o ningún elemento.
Anish Sana
23

El SQL que quieres es

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

Si quieres construir esto desde la pitón, puedes usar

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

La función de mapa transformará la lista en una lista de cadenas que se pueden unir mediante comas utilizando el método str.join .

Alternativamente:

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

si prefiere expresiones generadoras a la función de mapa.

ACTUALIZACIÓN: S. Lott menciona en los comentarios que los enlaces Python SQLite no admiten secuencias. En ese caso, es posible que desee

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

Generado por

sql_query = 'select name from studens where ' + ' or '.join(('id = ' + str(n) for n in l))
Blair Conrad
fuente
2
:-( El enlace Python SQLite no admite secuencias.
S.Lott
¿Oh? No me di cuenta Por otra parte, no me di cuenta de que íbamos a buscar una solución de enlace SQLite.
Blair Conrad el
Lo sentimos, no estoy sugiriendo ni implicando SQLite, es triste que los enlaces para listas no funcionen allí.
S.Lott
11

string. únete a los valores de la lista separados por comas y usa el operador de formato para formar una cadena de consulta.

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

(Gracias, blair-conrad )

gimel
fuente
2
Dado que este enfoque no utiliza la sustitución de parámetros de la base de datos, lo expone a ataques de inyección SQL. El %uso aquí es simplemente formato de cadena Python.
Nick Chammas
8

Me gusta la respuesta de 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)

Pero me di cuenta de esto:

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

Se puede reemplazar con:

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

Encuentro esto más directo si es menos inteligente y menos general. Aquí lse requiere tener una longitud (es decir, referirse a un objeto que define un __len__método), lo que no debería ser un problema. Pero el marcador de posición también debe ser un solo personaje. Para admitir un uso de marcador de posición de varios caracteres:

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

Solución para la respuesta @umounted, porque eso rompió con una tupla de un elemento, ya que (1,) no es SQL válido:

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

Otra solución para la cadena sql:

cursor.execute("select id from test where id in (%s)" % ('"'+'", "'.join(l)+'"'))
Ximix
fuente
55
Esta no es una mejor solución debido a la inyección 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)

Esto debería solucionar tu problema.

Rishabh Jain
fuente
2

Si está utilizando PostgreSQL con la biblioteca Psycopg2, puede dejar que su adaptación de tupla haga toda la interpolación de escape y cadena por usted, por ejemplo:

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

es decir, solo asegúrese de pasar el INparámetro como a tuple. si es un listpuede usar la = ANYsintaxis de matriz :

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

tenga en cuenta que ambos se convertirán en el mismo plan de consulta, por lo que debe usar el que sea más fácil. por ejemplo, si su lista viene en una tupla, use la primera, si están almacenadas en una lista, use la segunda.

Sam Mason
fuente
1

Por ejemplo, si desea la consulta sql:

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

Qué pasa:

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

Una solución más simple:

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

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

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

La :varnotación parece más simple. (Python 3.7)

usuario13476428
fuente
0

Esto utiliza la sustitución de parámetros y se ocupa del caso de la lista de valores únicos:

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
fuente