MySQL参数化查询

Spe*_*cto 76 python mysql bind-variables

我很难使用MySQLdb模块将信息插入到我的数据库中.我需要在表中插入6个变量.

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

""")
Run Code Online (Sandbox Code Playgroud)

有人可以帮我解决这里的语法吗?

Emi*_*l H 247

请注意对SQL查询使用字符串插值,因为它不会正确地转义输入参数,并会使您的应用程序对SQL注入漏洞开放.差异可能看起来微不足道,但实际上它是巨大的.

不正确(有安全问题)

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

正确(逃脱)

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

它增加了混淆,用于绑定SQL语句中的参数的修饰符在不同的DB API实现之间有所不同,并且mysql客户端库使用printf样式语法而不是更常用的'?' 标记(由例如使用python-sqlite).

  • 当它是一个单独的参数时记得保留逗号:`c.execute("SELECT*FROM foo WHERE bar =%s",(param1,))` (15认同)
  • 在许多情况下,正确的示例也会执行得更快 (11认同)
  • @Specto IMO始终保持正确和安全的实施方式是有意义的.它创造了正确的习惯和良好的编程文化.也没人知道您将来如何使用代码; 有人可以在以后用于其他系统或网站. (3认同)
  • 有关游标执行中参数的上下文(以及为什么仍然需要 %s),MySQLdb 游标的 api 参考位于 http://mysql-python.sourceforge.net/MySQLdb-1.2.2/public/MySQLdb。 Cursors.BaseCursor-class.html#execute (2认同)

Tre*_*out 44

你有几个选择.你会想要熟悉python的字符串iterpolation.当你想知道这样的事情时,你可能会在未来寻找更多成功的术语.

更好的查询:

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)
Run Code Online (Sandbox Code Playgroud)

考虑到您可能已经在对象或字典中拥有所有数据,第二种格式将更适合您.当你必须回来并在一年内更新这个方法时,还必须在字符串中计算"%s"外观:)

  • 如果必须在 SQL 语句中的多个位置使用给定的绑定变量,则字典方法看起来效果更好。使用位置方法,我们需要传递变量被引用的次数,这不是很理想。 (2认同)

小智 14

链接的文档提供以下示例:

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

所以你只需要根据自己的代码进行调整 - 例如:

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

        """, (var1, var2, var3, var4, var5, var6))
Run Code Online (Sandbox Code Playgroud)

(如果SongLength是数字,则可能需要使用%d而不是%s).

  • AFAIK你必须使用`%s`,无论哪种类型.@sheki:是的 (2认同)

daS*_*ong 5

实际上,即使您的变量(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,))
Run Code Online (Sandbox Code Playgroud)

在这个例子中,max_price = 5为什么在字符串中使用%s?因为MySQLdb会将其转换为SQL文字值,即字符串'5'.当它完成时,查询实际上会说"...... WHERE price <5".


FDS*_*FDS 5

作为所选答案的替代方案,并且使用与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()
Run Code Online (Sandbox Code Playgroud)

是辞典保持插入值.更新可以以相同的方式完成:

  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()
Run Code Online (Sandbox Code Playgroud)

  • 我印象深刻...你设法使python代码不可读! (7认同)