如何在Python的SQL语句中使用变量?


91

好的,所以我没有Python的经验。

我有以下Python代码:

cursor.execute("INSERT INTO table VALUES var1, var2, var3,")

其中var1是整数,var2var3是字符串。

如何在没有python的情况下编写变量名并将其作为查询文本的一部分?

Answers:


103
cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))

请注意,参数作为元组传递。

数据库API会正确地对变量进行转义和引用。注意不要使用字符串格式运算符(%),因为

  1. 它不会进行任何转义或引用。
  2. 它容易受到不受控制的字符串格式攻击,例如SQL注入

有趣的是,为什么它与var分开工作而不是与数组(var1,var2,var3)一起工作?
安多玛尔

根据DB API规范,看起来可以采用以下任何一种方式:python.org/dev/peps/pep-0249
Ayman Hourieh 09年

9
@thekashyap请仔细阅读。不安全的是使用字符串格式化运算符%。实际上,我在回答中是这样说的。
Ayman Hourieh 2014年

我的坏事。。我想像是a,% 而不是,在字符串和变量之间。由于种种原因,我无法撤消我的投票。。不使用%..
Kashyap 2014年

1
@eric回答说不要使用% 运算符格式化字符串。%字符串中的那些被cursor.execute直接使用,并且因为它知道它正在生成SQL,所以它可以做更多的事情来保护您。
Mark Ransom

66

允许不同的Python DB-API实现使用不同的占位符,因此您需要找出正在使用的占位符-可能是(例如,使用MySQLdb):

cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))

或(例如,使用Python标准库中的sqlite3):

cursor.execute("INSERT INTO table VALUES (?, ?, ?)", (var1, var2, var3))

或其他(在VALUES您拥有(:1, :2, :3),“命名样式” (:fee, :fie, :fo)(%(fee)s, %(fie)s, %(fo)s)在您将dict而不是map传递给的第二个参数之后execute)。paramstyle在您使用的DB API模块中检查字符串常量,并在http://www.python.org/dev/peps/pep-0249/上查找paramstyle,以查看所有参数传递样式是什么!


可以使用外部SQL脚本执行相同的操作吗?
Novitoll

46

很多方法。不要在实际代码中使用最明显的一个(%s带有%),它容易受到攻击

这里是从sqlite3的pydoc复制粘贴:

# Never do this -- insecure!
symbol = 'RHAT'
c.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)

# Do this instead
t = ('RHAT',)
c.execute('SELECT * FROM stocks WHERE symbol=?', t)
print c.fetchone()

# Larger example that inserts many records at a time
purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
             ('2006-04-05', 'BUY', 'MSFT', 1000, 72.00),
             ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
            ]
c.executemany('INSERT INTO stocks VALUES (?,?,?,?,?)', purchases)

如果您需要更多示例:

# Multiple values single statement/execution
c.execute('SELECT * FROM stocks WHERE symbol=? OR symbol=?', ('RHAT', 'MSO'))
print c.fetchall()
c.execute('SELECT * FROM stocks WHERE symbol IN (?, ?)', ('RHAT', 'MSO'))
print c.fetchall()
# This also works, though ones above are better as a habit as it's inline with syntax of executemany().. but your choice.
c.execute('SELECT * FROM stocks WHERE symbol=? OR symbol=?', 'RHAT', 'MSO')
print c.fetchall()
# Insert a single item
c.execute('INSERT INTO stocks VALUES (?,?,?,?,?)', ('2006-03-28', 'BUY', 'IBM', 1000, 45.00))

4
一些DB-API实现实际上将%s用作其变量-最著名的是PostgreSQL的psycopg2。请勿将%s与%运算符一起使用来替换字符串(尽管很容易)。如果出于便携性考虑,我们可以使用一种定义的标准方法为DB-API指定SQL参数,那真是太好了。
ThatAintWorking

24

http://www.amk.ca/python/writing/DB-API.html

在将变量的值简单地附加到语句时要小心:想象一个用户命名自己';DROP TABLE Users;'-这就是为什么您需要使用sql转义,这是Python在以适当的方式使用cursor.execute时为您提供的。网址中的示例是:

cursor.execute("insert into Attendees values (?, ?, ?)", (name,
seminar, paid) )

13
实际上,这不是SQL转义。它是变量绑定,它更简单,更直接。这些值在解析后绑定到SQL语句中,使其不受任何注入攻击的影响。
S.Lott

好吧,究竟是SQL转义还是变量绑定取决于数据库服务器/ DB-API驱动程序的优劣。我已经看到了一些在现实世界中广泛部署的生产数据库,这些数据库的DB-API驱动程序只是在转义,而不是在线上保持数据和代码的带外。不用说,我对那些所谓的“数据库”没有太多的尊重。
查尔斯·达菲
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.