lista python na consulta sql como parâmetro

124

Eu tenho uma lista python, digamos, l

l = [1,5,8]

Eu quero escrever uma consulta sql para obter os dados de todos os elementos da lista, digamos

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

Como eu faço isso?

Mohit Ranka
fonte

Respostas:

111

Até agora, as respostas foram modelar os valores em uma sequência SQL simples. Isso é absolutamente bom para números inteiros, mas se quisermos fazer isso por strings, obtemos o problema de escape.

Aqui está uma variante usando uma consulta parametrizada que funcionaria 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
fonte
11
','.join(placeholder * len(l))seria um pouco menor, enquanto ainda imho legível
ThiefMaster
7
@ Thiefmaster: sim, teria que ser ([placeholder]*len(l))no caso geral, pois o espaço reservado pode ter vários caracteres.
bobince
1
@bince, no meu caso, atribuí uma variável a = 'john' e b = 'snow' e a armazenei em uma tupla com o nome got = ['a, b'] e fiz a mesma consulta. Ao fazer isso, obtive o erroy, ou seja, Type Error: nem todos os argumentos foram convertidos durante a formatação da string. Como devo resolver isso?
TechJhola
2
Por que você deseja participar "manualmente" em vez de deixar esse trabalho para o dbapi? a chave é usar uma tupla em vez de uma lista ...
Alessandro Dentella
5
Com base nesta resposta, aqui está uma solução de linha única para Python 3.6 cursor.execute(f'SELECT name FROM students WHERE id IN ({','.join('?' for _ in l)})', l) . @bince, você também deve observar em sua solução que o uso ?é o caminho seguro para evitar a injeção de SQL. Há muitas respostas aqui vulneráveis, basicamente as que concatenam cadeias de caracteres em python.
Toto_tico 24/04
59

A maneira mais fácil é transformar a lista em tupleprimeiro lugar

t = tuple(l)
query = "select name from studens where id IN {}".format(t)
Amir Imani
fonte
1
Esta é realmente a resposta certa. Não sei por que foi ignorado. Primeiro, você configura a lista l, depois usa a tupla e depois passa a tupla para a consulta. bem feito.
Medwin
11
Como afirma @renstrm, isso não funciona se eu apenas contiver um elemento, você terminará com o ID IN (1,). O que é um erro de sintaxe.
Doubledown 30/05/19
1
@Boris se você não pode garantir que a sua entrada é sempre uma lista, você pode fazer algo semelhante a isto um forro stackoverflow.com/questions/38821586/... antes de passar o argumento para sua consulta
Amir Imani
10
Isso, como a maioria das respostas aqui, exceto a aceita, é vulnerável à injeção de SQL e não deve ser usado.
Bacon Bits
2
Aviso justo do @BaconBits, mas para alguns aplicativos em que a injeção de SQL não é um problema (por exemplo, análise de um banco de dados em que eu sou o único usuário), isso funcionará.
irene
26

Não complique, a solução para isso é simples.

l = [1,5,8]

l = tuple(l)

params = {'l': l}

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

insira a descrição da imagem aqui

Espero que isso tenha ajudado !!!

allsyed
fonte
10
Isso não funcionará se lapenas contiver um elemento, você vai acabar com id IN (1,). O que é um erro de sintaxe.
renstrm
3
Se l contiver apenas 1 elemento, certifique-se de que é uma tupla e funcionará. Esta solução é mais clara na minha opinião do que a aceita.
Alessandro Dentella 11/01
2
Mas ainda não funcionará se a tupla estiver vazia; portanto, deve haver uma verificação adicional antes da execução da consulta.
precisa saber é o seguinte
2
Isso não funciona para mim sqlite3. Em qual biblioteca você testou isso?
Nick Chammas
1
Boa solução, mas @renstrm e Никита-Конин têm razão em exigir verificações adicionais para quando a tupla tem um único elemento ou nenhum elemento.
Anish Sana
23

O SQL que você deseja é

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

Se você deseja construir isso a partir do python, você pode usar

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

A função map transformará a lista em uma lista de cadeias que podem ser coladas por vírgulas usando o 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)) + ')'

se você preferir expressões geradoras à função map.

UPDATE: S. Lott menciona nos comentários que as ligações Python SQLite não suportam sequências. Nesse caso, você pode querer

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

Gerado por

sql_query = 'select name from studens where ' + ' or '.join(('id = ' + str(n) for n in l))
Blair Conrad
fonte
2
:-( A ligação Python SQLite não suporta sequências.
S.Lott 12/11/2008
Ah? Eu não percebi. Por outro lado, não percebi que estávamos optando por uma solução de ligação SQLite.
Blair Conrad
Desculpe, não sugerindo ou implicando SQLite - é triste que as ligações para listas não funcionem lá.
S.Lott 12/11/08
11

string.join os valores da lista separados por vírgulas e use o operador format para formar uma string de consulta.

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

(Obrigado, Blair-Conrad )

gimel
fonte
2
Como essa abordagem não usa a substituição de parâmetros do banco de dados, expõe você a ataques de injeção de SQL. O que %está sendo usado aqui é apenas uma formatação simples de string Python.
Nick Chammas
8

Eu gosto da resposta 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)

Mas eu notei isso:

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

Pode ser substituído por:

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

Acho isso mais direto, se menos inteligente e menos geral. Aqui lé necessário ter um comprimento (ou seja, referir-se a um objeto que define um __len__método), o que não deve ser um problema. Mas o espaço reservado também deve ser um único caractere. Para oferecer suporte a um espaço reservado para vários caracteres, use:

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

Solução para resposta @umounted, porque ela quebrou com uma tupla de um elemento, pois (1,) não é 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,)]

Outra solução para sql string:

cursor.execute("select id from test where id in (%s)" % ('"'+'", "'.join(l)+'"'))
Ximix
fonte
5
Esta não é a melhor solução devido à injeção de 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)

Isso deve resolver seu problema.

Rishabh Jain
fonte
2

Se você estiver usando o PostgreSQL com a biblioteca Psycopg2, poderá permitir que sua adaptação à tupla faça toda a interpolação de escape e de string para você, por exemplo:

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

ou seja, apenas verifique se você está passando o INparâmetro como a tuple. se for um, listvocê pode usar a = ANYsintaxe da matriz :

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

observe que esses dois serão transformados no mesmo plano de consulta; portanto, você deve usar o que for mais fácil. por exemplo, se sua lista vem em uma tupla, use a primeira, se eles estão armazenados em uma lista, use a segunda.

Sam Mason
fonte
1

Por exemplo, se você deseja a consulta sql:

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

A respeito:

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

uma solução mais simples:

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

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

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

A :varnotação parece mais simples. (Python 3.7)

user13476428
fonte
0

Isso usa substituição de parâmetro e cuida do caso da 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
fonte