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.