MySQL参数化查询


80

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

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

""")

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

Answers:


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始终保持正确和安全的实施方式是有意义的。它会养成正确的习惯和良好的编程文化。同样,没人知道您的代码将来会如何使用。以后可以将其用于其他系统或网站。
罗曼·波德利诺夫

@BryanHunt您可以打开使用吗?带有某个参数的地方,但不建议这样做,因为它不会告诉您有关哪个参数去哪里的太多信息。(当然,出于相同的原因,我们也建议不要使用%s。)此处的更多信息:python.org/dev/peps/pep-0249/#paramstyle
kqr 2015年

1
来自php / pdo,如果使用printf样式%s标记,我感到非常困惑。感谢您清除烦恼!:)
Darragh Enright 2015年

18
当是单个参数时,请记住保持逗号: c.execute("SELECT * FROM foo WHERE bar = %s", (param1,))
Marius Lian

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)

考虑到您可能已经将所有数据存储在对象或词典中,因此第二种格式将更适合您。另外,当您不得不在一年内返回并更新此方法时,必须计数字符串中的“%s”出现也很糟糕:)


2
看起来,如果必须在SQL语句的多个位置使用给定的绑定变量,则字典方法会更好。使用位置方法时,我们需要将变量传递给它多次,这不是很理想。
codeforester

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是数字,则可能需要使用%d而不是%s)。


1
将在var1和var2具有诸如“或”之类的
字符的

3
AFAIK,%s无论哪种类型,都必须使用。@sheki:是的
ThiefMaster

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 price <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

1

第一个解决方案效果很好。我想在这里添加一个小细节。确保您要替换/更新的变量必须为str类型。我的mysql类型是十进制,但是我必须将参数变量设置为str才能执行查询。

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

0

这是另一种方法。它记录在MySQL官方网站上。 https://dev.mysql.com/doc/connector-python/zh-CN/connector-python-api-mysqlcursor-execute.html

从本质上讲,它使用的是@Trey Stout的答案相同的机制。但是,我觉得这更漂亮,更易读。

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.