How to use variables in SQL statement in Python?

I have the following Python code:

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

where var1 is an integer, var2 and var3 are strings.

How can I write the variable names without Python including them as part of the query text?

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

注意,参数以元组的形式传递。

The database API does proper escaping and quoting of variables. Be careful not to use the string formatting operator (%), because

  1. 它不做任何逃避或引用。
  2. it is prone to Uncontrolled string format attacks e.g. SQL 注入.

Http://www.amk.ca/python/writing/db-api.html

在简单地将变量值附加到语句中时要小心: 假设一个用户自称为 ';DROP TABLE Users;'—— 这就是为什么需要使用 SQL 转义,当您以一种体面的方式使用 cursor.execute时,Python 为您提供了 SQL 转义。网址中的例子是:

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

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的第二个参数)。检查您正在使用的 DB API 模块中的 paramstyle字符串常量,并在 http://www.python.org/dev/peps/pep-0249/中查找 paramstyle,以查看所有的参数传递样式是什么!

不要在实际代码中使用最明显的一种(%s%) ,它对 攻击是开放的。

这里复制粘贴 来自 sqlite3的 pydoc 的 href = “ https://docs.python.org/3.8/library/sqlite3.html”rel = “ nofollow noReferrer”> :

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


# Do this instead
t = ('RHAT',)
cur.execute('SELECT * FROM stocks WHERE symbol=?', t)
print(cur.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),
]
cur.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))

提供单个值的语法可能会让缺乏经验的 Python 用户感到困惑。

给出查询

INSERT INTO mytable (fruit) VALUES (%s)

通常 * ,传递给 cursor.execute的值必须包装成一个有序的序列,如 tuple名单,即使该值本身是一个单元素,所以我们必须提供一个单元素元组,如下所示: (value,)

cursor.execute("""INSERT INTO mytable (fruit) VALUES (%s)""", ('apple',))

Passing a single string

cursor.execute("""INSERT INTO mytable (fruit) VALUES (%s)""", ('apple'))

将导致一个错误,该错误因 DB-API 连接器而异,例如

  • 心理学家2:

    TypeError: 并非在字符串格式化过程中转换的所有参数

  • Sqlite3

    错误: 提供的绑定数不正确。当前语句使用1,提供了5个

  • mysql.connector

    错误。编程错误: 1064(42000) : 您的 SQL 语法有错误;


* pymysql 连接器处理单个字符串参数而不出错。但是,最好将字符串包装在元组中,即使它是单个的,因为

  • 如果切换连接器包,则不需要更改代码
  • 你保持一个一致的心理模型,查询参数是一系列的对象而不是一个单一的对象。