Python-Liste in SQL-Abfrage als Parameter

122

Ich habe eine Python-Liste, sag ich

l = [1,5,8]

Ich möchte eine SQL-Abfrage schreiben, um beispielsweise die Daten für alle Elemente der Liste abzurufen

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

Wie schaffe ich das?

Mohit Ranka
quelle

Antworten:

111

Bisher wurden die Werte in eine einfache SQL-Zeichenfolge umgewandelt. Das ist absolut in Ordnung für ganze Zahlen, aber wenn wir es für Strings tun wollten, bekommen wir das Escape-Problem.

Hier ist eine Variante mit einer parametrisierten Abfrage, die für beide funktioniert:

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
quelle
11
','.join(placeholder * len(l))wäre etwas kürzer während noch lesbar imho
ThiefMaster
7
@Thiefmaster: Ja, müsste ([placeholder]*len(l))im allgemeinen Fall sein, da der Platzhalter aus mehreren Zeichen bestehen kann.
Bobince
1
@bobince In meinem Fall habe ich eine Variable a = 'john' und b = 'snow' zugewiesen und sie in einem Tupel mit dem Namen got = ['a, b'] gespeichert und dieselbe Abfrage durchgeführt. Dabei habe ich den Fehler "Fehler" erhalten, dh nicht alle Argumente, die während der Formatierung der Zeichenfolge konvertiert wurden. Wie soll ich es
lösen
2
Warum möchten Sie "von Hand" beitreten, anstatt diese Arbeit dem dbapi zu überlassen? Der Schlüssel ist, ein Tupel anstelle einer Liste zu verwenden ...
Alessandro Dentella
5
Basierend auf dieser Antwort ist hier eine einzeilige Lösung für Python 3.6 cursor.execute(f'SELECT name FROM students WHERE id IN ({','.join('?' for _ in l)})', l) . @bobince, Sie sollten in Ihrer Lösung auch bemerken, dass die Verwendung ?der sichere Weg ist, um SQL-Injection zu vermeiden. Hier gibt es viele Antworten, die anfällig sind, im Grunde alle, die Zeichenfolgen in Python verketten.
toto_tico
59

Am einfachsten ist es, die Liste an die tupleerste Stelle zu setzen

t = tuple(l)
query = "select name from studens where id IN {}".format(t)
Amir Imani
quelle
1
Das ist eigentlich die richtige Antwort. Ich bin nicht sicher, warum es ignoriert wurde. Zuerst richten Sie die Liste l ein, verwenden dann Tupel und übergeben das Tupel an die Abfrage. gut gemacht.
MEdwin
11
Wie von @renstrm angegeben, funktioniert dies nicht, wenn ich nur ein Element enthält, erhalten Sie die ID IN (1,). Welches ist ein Syntaxfehler.
Doubledown
1
@Boris Wenn Sie nicht garantieren können, dass Ihre Eingabe immer eine Liste ist, können Sie etwas Ähnliches wie diesen Einzeiler stackoverflow.com/questions/38821586/… tun, bevor Sie das Argument an Ihre Abfrage übergeben
Amir Imani
10
Dies ist, wie die meisten anderen Antworten als die akzeptierte, anfällig für SQL-Injection und sollte nicht verwendet werden.
Bacon Bits
2
@ BaconBits Faire Warnung, aber für einige Anwendungen, bei denen die SQL-Injection kein Problem darstellt (z. B. die Analyse einer Datenbank, bei der ich der einzige Benutzer bin), reicht dies aus.
Irene
26

Komplizieren Sie es nicht, die Lösung hierfür ist einfach.

l = [1,5,8]

l = tuple(l)

params = {'l': l}

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

Geben Sie hier die Bildbeschreibung ein

Ich hoffe das hat geholfen !!!

allsyed
quelle
10
Dies funktioniert nicht, wenn lnur ein Element enthalten ist id IN (1,). Welches ist ein Syntaxfehler.
Renstrm
3
Wenn l nur 1 Element enthält, stellen Sie sicher, dass es ein Tupel ist und es funktioniert. Diese Lösung ist meiner Meinung nach klarer als die akzeptierte.
Alessandro Dentella
2
Es funktioniert jedoch immer noch nicht, wenn das Tupel leer ist. Daher muss vor der Ausführung der Abfrage eine zusätzliche Überprüfung durchgeführt werden.
17кита Конин
2
Das funktioniert bei mir nicht sqlite3. Mit welcher Bibliothek haben Sie das getestet?
Nick Chammas
1
Gute Lösung, aber @renstrm und Никита-Конин erfordern zu Recht zusätzliche Überprüfungen, wenn das Tupel ein einzelnes Element oder keine Elemente enthält.
Anish Sana
23

Das gewünschte SQL ist

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

Wenn Sie dies aus der Python konstruieren möchten, können Sie verwenden

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

Die Map- Funktion wandelt die Liste in eine Liste von Zeichenfolgen um, die mit der Methode str.join durch Kommas zusammengeklebt werden können .

Alternative:

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

Wenn Sie Generatorausdrücke der Kartenfunktion vorziehen .

AKTUALISIEREN: S. Lott erwähnt in den Kommentaren, dass die Python SQLite-Bindungen keine Sequenzen unterstützen. In diesem Fall möchten Sie vielleicht

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

Erstellt von

sql_query = 'select name from studens where ' + ' or '.join(('id = ' + str(n) for n in l))
Blair Conrad
quelle
2
:-( Python SQLite-Bindung unterstützt keine Sequenzen.
S.Lott
Oh? Ich habe es nicht bemerkt. Andererseits wusste ich nicht, dass wir uns für eine SQLite-Bindungslösung entscheiden.
Blair Conrad
Entschuldigung, SQLite nicht vorschlagen oder implizieren - nur traurig, dass Bindungen für Listen dort nicht funktionieren.
S.Lott
11

String.Join die Liste Wert durch Kommata getrennt, und verwenden Sie das Format Operator eine Abfrage - Zeichenfolge zu bilden.

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

(Danke, Blair-Conrad )

Gimel
quelle
2
Da dieser Ansatz keine Datenbankparametersubstitution verwendet, sind Sie SQL-Injection-Angriffen ausgesetzt. Das %Wesen verwendet hier ist einfach nur Python - String formatiert wird .
Nick Chammas
8

Ich mag die Antwort von 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)

Aber mir ist das aufgefallen:

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

Kann ersetzt werden durch:

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

Ich finde das direkter, wenn auch weniger klug und weniger allgemein. Hier lmuss eine Länge vorhanden sein (dh auf ein Objekt verweisen, das eine __len__Methode definiert ), was kein Problem sein sollte. Platzhalter müssen aber auch ein einzelnes Zeichen sein. So unterstützen Sie einen Platzhalter mit mehreren Zeichen:

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

Lösung für @umounted Antwort, da dies mit einem Ein-Element-Tupel brach, da (1,) kein gültiges SQL ist:

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

Andere Lösung für SQL-Zeichenfolge:

cursor.execute("select id from test where id in (%s)" % ('"'+'", "'.join(l)+'"'))
Ximix
quelle
5
Dies ist aufgrund der SQL-Injektion keine bessere Lösung.
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)

Dies sollte Ihr Problem lösen.

Rishabh Jain
quelle
2

Wenn Sie PostgreSQL mit der psycopg2 Bibliothek verwenden können Sie sein lassen Tupel Anpassung für Sie alle entkommen und String - Interpolation tun, zum Beispiel:

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

Stellen Sie einfach sicher, dass Sie den INParameter als übergeben tuple. Wenn es ein ist list, können Sie die = ANYArray-Syntax verwenden :

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

Beachten Sie, dass beide in denselben Abfrageplan umgewandelt werden, sodass Sie nur den einfacheren verwenden sollten. Wenn Ihre Liste beispielsweise in einem Tupel vorliegt, verwenden Sie das erstere. Wenn sie in einer Liste gespeichert sind, verwenden Sie das letztere.

Sam Mason
quelle
1

Wenn Sie beispielsweise die SQL-Abfrage möchten:

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

Wie wäre es mit:

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

eine einfachere Lösung:

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

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

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

Die :varNotation scheint einfacher. (Python 3.7)

user13476428
quelle
0

Dies verwendet die Parametersubstitution und kümmert sich um den Fall der Einzelwertliste:

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
quelle