MySQLパラメータ化クエリ


80

MySQLdbモジュールを使用してデータベースに情報を挿入するのに苦労しています。テーブルに6つの変数を挿入する必要があります。

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

""")

誰かがここの構文を手伝ってくれますか?

回答:


262

SQLクエリに文字列補間を使用すると、入力パラメータが正しくエスケープされず、アプリケーションがSQLインジェクションの脆弱性にさらされることに注意してください。違いは些細なことのように思えるかもしれませんが、実際にはそれは巨大です。

正しくない(セキュリティの問題がある)

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

正解(エスケープあり)

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

これは、SQLステートメントのパラメーターをバインドするために使用される修飾子が異なるDB API実装間で異なり、mysqlクライアントライブラリprintfがより一般的に受け入れられている「?」の代わりにスタイル構文を使用するという混乱を助長します。マーカー(例で使用python-sqlite)。


3
@Specto IMOは、常に適切で安全な実装方法を維持することが理にかなっています。それは正しい習慣とプログラミングの良い文化を生み出します。また、あなたのコードが将来どのように使用されるかは誰にもわかりません。誰かが後で他のシステムやウェブサイトに使用できます。
ローマンポドリノフ2013年

@BryanHunt?の使用をオンにすることができます どこかに引数がありますが、どの引数がどこに行くのかについてはあまり説明されていないため、お勧めしません。(もちろん、%sについても同じことが言えますが、同じ理由でお勧めしません。)詳細はこちら:python.org/dev/peps/pep-0249/#paramstyle
kqr

1
php / pdoから来たので、printfスタイル%sマーカーの場合は非常に混乱し、脆弱なクエリを書いているのではないかと恐れていました。その心配を片付けてくれてありがとう!:)
Darragh Enright 2015年

18
それは、単一のパラメータはカンマを保つために覚えています: c.execute("SELECT * FROM foo WHERE bar = %s", (param1,))
マリウスリアン

1
(%Sが依然として必要とされている理由と)実行カーソルのパラメータのコンテキストのために、MySQLdbはカーソル用のAPIリファレンスであるmysql-python.sourceforge.net/MySQLdb-1.2.2/public/...
RedBarron

50

利用可能なオプションがいくつかあります。Pythonの文字列の反復に慣れたいと思うでしょう。これは、このようなことを知りたいときに、将来検索でより成功する可能性のある用語です。

クエリに適しています:

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)

おそらくすべてのデータがすでにオブジェクトまたはディクショナリにあることを考えると、2番目の形式の方が適しています。また、1年以内に戻ってこのメソッドを更新する必要がある場合、文字列内の「%s」の出現をカウントする必要があるのは残念です:)


2
特定のバインド変数をSQLステートメントの複数の場所で使用する必要がある場合は、ディクショナリアプローチの方がうまく機能するようです。位置的アプローチでは、参照される回数だけ変数を渡す必要がありますが、これはあまり望ましくありません。
codeforester 2018

15

リンクされたドキュメントは次の例を示しています。

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

したがって、これを独自のコードに適合させる必要があります-例:

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

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

(SongLengthが数値の場合、%sの代わりに%dを使用する必要がある場合があります)。


1
VAR1とVAR2は'のような"またはcharectersを持っているこの作品はなります。
Shekiの

3
AFAIKは、%sどのタイプでも使用する必要があります。@sheki:はい
ThiefMaster 2016年

7

実際、変数(SongLength)が数値であっても、パラメーターを正しくバインドするには、%sでフォーマットする必要があります。%dを使用しようとすると、エラーが発生します。これはこのリンクからの小さな抜粋ですhttp://mysql-python.sourceforge.net/MySQLdb.html

クエリを実行するには、最初にカーソルが必要です。次に、カーソルに対してクエリを実行できます。

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

この例では、max_price = 5では、なぜ文字列に%sを使用するのでしょうか。MySQLdbはそれをSQLリテラル値(文字列「5」)に変換するためです。完了すると、クエリは実際には「... WHERE価格<5」と表示されます。


ええ、これは奇妙なことです...「printf」形式は...実際にはprintf形式であり、どこでも%sを使用するだけではないと思います。
fthinker 2013年

6

選択した回答の代わりに、Marcelと同じ安全なセマンティクスを使用して、Python辞書を使用して値を指定するコンパクトな方法を次に示します。挿入する列を追加または削除するときに、簡単に変更できるという利点があります。

  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()

ここで、metaは、挿入する値を保持する辞書です。更新は同じ方法で行うことができます:

  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()

7
私は感銘を受けました...あなたはなんとかPythonコードを読めなくすることができました!
Iharob Al Asimi 2016

1

最初の解決策はうまく機能します。ここに1つの小さな詳細を追加したいと思います。置換/更新しようとしている変数がstr型である必要があることを確認してください。私のmysqlタイプは10進数ですが、クエリを実行できるようにするには、パラメータ変数をstrとして作成する必要がありました。

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

0

これを行う別の方法があります。これは、MySQLの公式Webサイトに文書化されています。 https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-execute.html

精神的には、@ TreyStoutの回答と同じメカニズムを使用しています。しかし、私はこれがより美しく、より読みやすいと思います。

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)

そして、変数の必要性をよりよく説明するために:

注意:エスケープが行われていることに注意してください。

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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.