lista pythona w kwerendzie sql jako parametr


125

Mam listę Pythona, powiedz l

l = [1,5,8]

Chcę napisać zapytanie sql, aby uzyskać dane dla, powiedzmy, wszystkich elementów listy

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

Jak to osiągnąć?

Odpowiedzi:


114

Odpowiedzi do tej pory umieszczały wartości w postaci zwykłego ciągu SQL. Jest to absolutnie w porządku w przypadku liczb całkowitych, ale jeśli chcemy to zrobić dla łańcuchów, pojawia się problem ze ucieczką.

Oto wariant używający sparametryzowanego zapytania, który zadziała w obu przypadkach:

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))byłby nieco krótszy, a nadal czytelny
imho

7
@Thiefmaster: tak, ([placeholder]*len(l))w ogólnym przypadku musi to być, ponieważ symbol zastępczy może mieć wiele znaków.
bobince

1
@bobince w moim przypadku przypisałem zmienną a = 'john' i b = 'snow' i umieściłem ją w krotce o nazwie get = ['a, b'] i wykonałem to samo zapytanie. Robiąc to, otrzymałem erroy, tj. Błąd typu: nie wszystkie argumenty zostały przekonwertowane podczas formatowania ciągu. Jak mam to rozwiązać
TechJhola

2
Dlaczego chcesz dołączyć „ręcznie”, zamiast pozostawić tę pracę dbapi? kluczem jest użycie krotki, a nie listy ...
Alessandro Dentella

5
Opierając się na tej odpowiedzi, przedstawiono rozwiązanie jednowierszowe dla języka Python 3.6 cursor.execute(f'SELECT name FROM students WHERE id IN ({','.join('?' for _ in l)})', l) . @bobince, powinieneś również zauważyć w swoim rozwiązaniu, że używanie ?jest bezpiecznym sposobem na uniknięcie iniekcji SQL. Jest tutaj wiele odpowiedzi, które są podatne na ataki, w zasadzie te, które łączą ciągi znaków w Pythonie.
toto_tico

60

Najłatwiej jest obrócić listę na tuplepierwszą

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

1
To jest właściwie prawidłowa odpowiedź. Nie jestem pewien, dlaczego został zignorowany. Najpierw konfigurujesz listę l, następnie używasz krotki, a następnie przekazujesz krotkę do zapytania. dobra robota.
MEdwin

11
Jak stwierdzono przez @renstrm, to nie działa, jeśli zawiera tylko jeden element, otrzymasz identyfikator IN (1,). Co jest błędem składniowym.
Doubledown

1
@Amirhos Imani, a co jeśli nie wiesz, ile masz elementów?
Boris

10
To, podobnie jak większość odpowiedzi tutaj innych niż zaakceptowana, jest podatne na wstrzyknięcie SQL i nie powinno być używane.
Bacon Bits

2
@BaconBits Uczciwe ostrzeżenie, ale w przypadku niektórych aplikacji, w których iniekcja SQL nie stanowi problemu (na przykład analiza bazy danych, w której jestem jedynym użytkownikiem), wystarczy.
Irene

26

Nie komplikuj tego, rozwiązanie tego jest proste.

l = [1,5,8]

l = tuple(l)

params = {'l': l}

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

wprowadź opis obrazu tutaj

Mam nadzieję, że to pomogło !!!


10
To nie zadziała, jeśli lzawiera tylko jeden element, skończysz id IN (1,). Co jest błędem składniowym.
renstrm

3
Jeśli zawiera tylko 1 element, upewnij się, że jest to krotka i BĘDZIE działać. To rozwiązanie jest moim zdaniem jaśniejsze niż przyjęte.
Alessandro Dentella

2
Ale nadal nie zadziała, jeśli krotka jest pusta, więc przed wykonaniem zapytania musi nastąpić dodatkowe sprawdzenie.
Никита Конин

2
To mi nie pasuje sqlite3. Z jaką biblioteką to przetestowałeś?
Nick Chammas

1
Dobre rozwiązanie, ale @renstrm i Никита-Конин mają rację, jeśli chodzi o wymaganie dodatkowego sprawdzenia, kiedy krotka zawiera pojedynczy element lub nie ma żadnych elementów.
Anish Sana

23

Żądany SQL to

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

Jeśli chcesz zbudować to z Pythona, możesz użyć

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

Funkcja map przekształci listę w listę ciągów, które można skleić przecinkami przy użyciu metody str.join .

Alternatywnie:

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

jeśli wolisz wyrażenia generatora od funkcji mapy.

UPDATE: S. Lott wspomina w komentarzach, że powiązania Python SQLite nie obsługują sekwencji. W takim przypadku możesz chcieć

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

Wygenerowane przez

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

2
:-( Wiązanie Python SQLite nie obsługuje sekwencji.
S.Lott,

O? Nie zdawałem sobie sprawy. Z drugiej strony nie zdawałem sobie sprawy, że wybieramy rozwiązanie wiążące SQLite.
Blair Conrad

Przepraszamy, nie sugeruję ani nie sugeruję SQLite - po prostu smutne, że wiązania dla list tam nie działają.
S.Lott,

11

ciąg. dołącz wartości listy oddzielone przecinkami i użyj operatora formatu, aby utworzyć ciąg zapytania.

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

(Dzięki, blair-conrad )


2
Ponieważ to podejście nie wykorzystuje podstawiania parametrów bazy danych, naraża Cię na ataki polegające na iniekcji SQL. %Byt używany tutaj jest po prostu Python ciąg formatowania.
Nick Chammas

8

Podoba mi się odpowiedź Bobince'a:

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)

Ale zauważyłem to:

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

Można wymienić na:

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

Uważam, że jest to bardziej bezpośrednie, jeśli mniej sprytne i mniej ogólne. Tutaj lwymagana jest długość (tj. Odwołanie się do obiektu, który definiuje __len__metodę), co nie powinno stanowić problemu. Ale symbol zastępczy musi również być pojedynczym znakiem. Aby obsługiwać wieloznakowy symbol zastępczy:

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

2

Rozwiązanie dla odpowiedzi @umounted, ponieważ zostało przerwane z jednoelementową krotką, ponieważ (1,) nie jest poprawnym kodem SQL .:

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

Inne rozwiązanie dla ciągu sql:

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

5
Nie jest to lepsze rozwiązanie z powodu wtrysku 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)

To powinno rozwiązać twój problem.


2

Jeśli używasz PostgreSQL z biblioteką Psycopg2, możesz pozwolić jej adaptacji krotek na wykonanie wszystkich znaków ucieczki i interpolacji ciągów, np .:

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

czyli po prostu upewnij się, że przekazujesz INparametr jako plik tuple. jeśli to a list, możesz użyć = ANYskładni tablicy :

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

zwróć uwagę, że oba zostaną przekształcone w ten sam plan zapytań, więc powinieneś po prostu użyć tego, który jest łatwiejszy. np. jeśli twoja lista znajduje się w krotce, użyj pierwszej, jeśli jest przechowywana w liście, użyj drugiej.


1

Na przykład, jeśli chcesz, aby zapytanie sql:

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

Co powiesz na:

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

1

prostsze rozwiązanie:

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)

:varZapis wydaje się prostsze. (Python 3.7)


0

To używa podstawiania parametrów i zajmuje się przypadkiem listy pojedynczej wartości:

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),))
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.