MySQL-parametrisierte Abfragen

80

Es fällt mir schwer, mit dem MySQLdb-Modul Informationen in meine Datenbank einzufügen. Ich muss 6 Variablen in die Tabelle einfügen.

cursor.execute ("""
    INSERT INTO Songs (SongName, SongArtist, SongAlbum, SongGenre, SongLength, SongLocation)
    VALUES
        (var1, var2, var3, var4, var5, var6)

""")

Kann mir hier jemand bei der Syntax helfen?

Specto
quelle

Antworten:

262

Achten Sie auf die Verwendung der Zeichenfolgeninterpolation für SQL-Abfragen, da die Eingabeparameter nicht korrekt ausgeblendet werden und Ihre Anwendung für SQL-Injection-Schwachstellen offen bleibt. Der Unterschied mag trivial erscheinen, aber in Wirklichkeit ist er riesig .

Falsch (mit Sicherheitsproblemen)

c.execute("SELECT * FROM foo WHERE bar = %s AND baz = %s" % (param1, param2))

Richtig (mit Flucht)

c.execute("SELECT * FROM foo WHERE bar = %s AND baz = %s", (param1, param2))

Dies trägt zur Verwirrung bei, dass die zum Binden von Parametern in einer SQL-Anweisung verwendeten Modifikatoren zwischen verschiedenen DB-API-Implementierungen variieren und dass die MySQL-Clientbibliothek die Stilsyntax printfanstelle des allgemein akzeptierten '?' Marker (verwendet von zB python-sqlite).

Emil H.
quelle
3
@Specto IMO ist es sinnvoll, immer die richtigen und sicheren Implementierungsmethoden einzuhalten. Es schafft die richtigen Gewohnheiten und eine gute Programmierkultur. Außerdem weiß niemand, wie Ihr Code in Zukunft verwendet wird. jemand kann es später für ein anderes System oder eine andere Website verwenden.
Roman Podlinov
@BryanHunt Sie können die Verwendung von aktivieren? mit einem Argument irgendwo, aber es wird entmutigt, weil es Ihnen nicht viel darüber sagt, welches Argument wohin geht. (Dasselbe gilt natürlich auch für% s, was aus demselben Grund nicht empfohlen wird
kqr
1
Von php / pdo Ich war mega-verwirrt , wenn der printfStil %sMarker, und ich war sorta Angst , dass ich verwundbar Abfragen schrieb. Vielen Dank, dass Sie diese Sorgen ausgeräumt haben! :)
Darragh Enright
18
Wenn es sich um einen einzelnen Parameter handelt, denken Sie daran, das Komma beizubehalten: c.execute("SELECT * FROM foo WHERE bar = %s", (param1,))
Marius Lian
1
Für den Kontext zu den Parametern in der Cursorausführung (und warum% s noch benötigt wird) befindet sich die API-Referenz für den MySQLdb-Cursor unter mysql-python.sourceforge.net/MySQLdb-1.2.2/public/…
RedBarron
50

Sie haben einige Optionen zur Verfügung. Sie sollten sich mit der String-Iterpolation von Python vertraut machen. Nach diesem Begriff könnten Sie in Zukunft erfolgreicher suchen, wenn Sie solche Dinge wissen möchten.

Besser für Fragen:

some_dictionary_with_the_data = {
    'name': 'awesome song',
    'artist': 'some band',
    etc...
}
cursor.execute ("""
            INSERT INTO Songs (SongName, SongArtist, SongAlbum, SongGenre, SongLength, SongLocation)
            VALUES
                (%(name)s, %(artist)s, %(album)s, %(genre)s, %(length)s, %(location)s)

        """, some_dictionary_with_the_data)

Da Sie wahrscheinlich bereits alle Ihre Daten in einem Objekt oder Wörterbuch haben, passt das zweite Format besser zu Ihnen. Es ist auch schade, "% s" -Auftritte in einer Zeichenfolge zählen zu müssen, wenn Sie zurückkommen und diese Methode in einem Jahr aktualisieren müssen :)

Trey Stout
quelle
2
Der Wörterbuchansatz funktioniert anscheinend besser, wenn eine bestimmte Bindevariable an mehr als einer Stelle in der SQL-Anweisung verwendet werden muss. Beim Positionsansatz müssen wir die Variable so oft übergeben, wie sie referenziert wird, was nicht sehr wünschenswert ist.
Codeforester
15

Die verknüpften Dokumente geben das folgende Beispiel:

   cursor.execute ("""
         UPDATE animal SET name = %s
         WHERE name = %s
       """, ("snake", "turtle"))
   print "Number of rows updated: %d" % cursor.rowcount

Sie müssen dies also nur an Ihren eigenen Code anpassen - Beispiel:

cursor.execute ("""
            INSERT INTO Songs (SongName, SongArtist, SongAlbum, SongGenre, SongLength, SongLocation)
            VALUES
                (%s, %s, %s, %s, %s, %s)

        """, (var1, var2, var3, var4, var5, var6))

(Wenn SongLength numerisch ist, müssen Sie möglicherweise% d anstelle von% s verwenden.)

Marcel Guzman
quelle
1
Wird dies funktionieren, wenn var1 und var2 Zeichen wie "oder" haben?
sheki
3
AFAIK müssen Sie %sdort verwenden, egal welcher Typ. @ Sheki: Ja
ThiefMaster
7

Selbst wenn Ihre Variable (SongLength) numerisch ist, müssen Sie sie mit% s formatieren, um den Parameter korrekt zu binden. Wenn Sie versuchen,% d zu verwenden, wird eine Fehlermeldung angezeigt. Hier ist ein kleiner Auszug aus diesem Link http://mysql-python.sourceforge.net/MySQLdb.html :

Um eine Abfrage auszuführen, benötigen Sie zuerst einen Cursor, und dann können Sie Abfragen darauf ausführen:

c=db.cursor()
max_price=5
c.execute("""SELECT spam, eggs, sausage FROM breakfast
          WHERE price < %s""", (max_price,))

In diesem Beispiel ist max_price = 5 Warum wird dann% s in der Zeichenfolge verwendet? Weil MySQLdb es in einen SQL-Literalwert konvertiert, der die Zeichenfolge '5' ist. Wenn es fertig ist, sagt die Abfrage tatsächlich "... WHERE Preis <5".

daSong
quelle
Ja, das ist in Ordnung seltsam ... Sie denken, dass das "printf" -Format bedeuten würde ... tatsächlich das printf-Format und nicht nur die Verwendung von% s überall.
Denker
6

Als Alternative zur gewählten Antwort und mit der gleichen sicheren Semantik wie bei Marcel gibt es hier eine kompakte Möglichkeit, die Werte mithilfe eines Python-Wörterbuchs anzugeben. Es hat den Vorteil, dass es einfach geändert werden kann, wenn Sie Spalten zum Einfügen hinzufügen oder entfernen:

  meta_cols=('SongName','SongArtist','SongAlbum','SongGenre')
  insert='insert into Songs ({0}) values ({1})'.
        .format(','.join(meta_cols), ','.join( ['%s']*len(meta_cols) ))
  args = [ meta[i] for i in meta_cols ]
  cursor=db.cursor()
  cursor.execute(insert,args)
  db.commit()

Wobei meta das Wörterbuch ist, das die einzufügenden Werte enthält. Das Update kann auf folgende Weise erfolgen:

  meta_cols=('SongName','SongArtist','SongAlbum','SongGenre')
  update='update Songs set {0} where id=%s'.
        .format(','.join([ '{0}=%s'.format(c) for c in meta_cols ]))
  args = [ meta[i] for i in meta_cols ]
  args.append( songid )
  cursor=db.cursor()
  cursor.execute(update,args)
  db.commit()
FDS
quelle
7
Ich bin beeindruckt ... Sie haben es geschafft, Python-Code unlesbar zu machen!
Iharob Al Asimi
1

Die erste Lösung funktioniert gut. Ich möchte hier ein kleines Detail hinzufügen. Stellen Sie sicher, dass die Variable, die Sie ersetzen / aktualisieren möchten, vom Typ str sein muss. Mein MySQL-Typ ist dezimal, aber ich musste die Parametervariable als str festlegen, um die Abfrage ausführen zu können.

temp = "100"
myCursor.execute("UPDATE testDB.UPS SET netAmount = %s WHERE auditSysNum = '42452'",(temp,))
myCursor.execute(var)
Parth Shah
quelle
0

Hier ist eine andere Möglichkeit, dies zu tun. Es ist auf der offiziellen MySQL-Website dokumentiert. https://dev.mysql.com/doc/connector-python/de/connector-python-api-mysqlcursor-execute.html

Im Geiste verwendet es die gleiche Mechanik wie die Antwort von @Trey Stout. Ich finde dies jedoch schöner und lesbarer.

insert_stmt = (
  "INSERT INTO employees (emp_no, first_name, last_name, hire_date) "
  "VALUES (%s, %s, %s, %s)"
)
data = (2, 'Jane', 'Doe', datetime.date(2012, 3, 23))
cursor.execute(insert_stmt, data)

Und um den Bedarf an Variablen besser zu veranschaulichen:

NB: Beachten Sie die Flucht.

employee_id = 2
first_name = "Jane"
last_name = "Doe"

insert_stmt = (
  "INSERT INTO employees (emp_no, first_name, last_name, hire_date) "
  "VALUES (%s, %s, %s, %s)"
)
data = (employee_id, conn.escape_string(first_name), conn.escape_string(last_name), datetime.date(2012, 3, 23))
cursor.execute(insert_stmt, data)
Djidiouf
quelle