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

use*_*606 79 python sql

好的,所以我不是那种经验丰富的Python.

我有以下Python代码:

cursor.execute("INSERT INTO table VALUES var1, var2, var3,")
Run Code Online (Sandbox Code Playgroud)

where var1是整数,var2&var3是字符串.

如何在没有python的情况下编写变量名,包括它们作为查询文本的一部分?

Aym*_*ieh 84

cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))
Run Code Online (Sandbox Code Playgroud)

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

数据库API可以正确转义和引用变量.注意不要使用字符串格式化operator(%),因为

  1. 它没有做任何逃避或引用.
  2. 它很容易受到不受控制的字符串格式攻击,例如SQL注入.

  • @thekashyap仔细阅读.什么是不安全的是使用字符串格式化运算符`%`.事实上,我在答案中这样说. (8认同)
  • 被否决,因为答案说不要使用“%”,但使用了三次。更多解释会很棒。 (2认同)
  • @eric,答案是不要使用 `%` *运算符* 来格式化字符串。字符串中的那些“%”由“cursor.execute”直接使用,并且由于它知道自己正在生成 SQL,因此它可以采取更多措施来保护您。 (2认同)

Ale*_*lli 61

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

cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))
Run Code Online (Sandbox Code Playgroud)

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

cursor.execute("INSERT INTO table VALUES (?, ?, ?)", (var1, var2, var3))
Run Code Online (Sandbox Code Playgroud)

或其他人(在VALUES你可以拥有(:1, :2, :3),或"命名样式" (:fee, :fie, :fo)(%(fee)s, %(fie)s, %(fo)s)你传递字典而不是地图作为第二个参数execute).检查paramstyle您正在使用的DB API模块中的字符串常量,并在http://www.python.org/dev/peps/pep-0249/上查找paramstyle 以查看所有参数传递样式是什么!


Kas*_*yap 37

很多种方法.不要在实际代码中使用最明显的(%swith %),它可以攻击.

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

如果您需要更多示例:

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

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

Num*_*ock 20

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

当你只是在你的语句中附加变量值时要小心:想象一下用户自己命名';DROP TABLE Users;'- 这就是你需要使用sql escaping的原因,当你以一种体面的方式使用cursor.execute时,Python会为你提供.url中的示例是:

cursor.execute("insert into Attendees values (?, ?, ?)", (name,
seminar, paid) )
Run Code Online (Sandbox Code Playgroud)

  • 实际上,它不是SQL转义.它是变量绑定,它更简单,更直接.解析后,这些值被绑定到SQL语句中,使其免受任何注入攻击. (10认同)
  • “小鲍比桌子,我们叫他。” 清理您的输入。https://xkcd.com/327/ (4认同)

sna*_*erb 8

对于没有经验的 Python 用户来说,提供单个值的语法可能会令人困惑。

鉴于查询

INSERT INTO mytable (fruit) VALUES (%s)
Run Code Online (Sandbox Code Playgroud)

一般来说,即使值本身是单例,传递给的值cursor.execute也必须包装在有序序列(例如元组列表)中,因此我们必须提供单个元素元组,如下所示:(value,)

cursor.execute("""INSERT INTO mytable (fruit) VALUES (%s)""", ('apple',))
Run Code Online (Sandbox Code Playgroud)

传递单个字符串

cursor.execute("""INSERT INTO mytable (fruit) VALUES (%s)""", ('apple'))
Run Code Online (Sandbox Code Playgroud)

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

  • 心理咨询2:

    类型错误:在字符串格式化期间并非所有参数都被转换

  • sqlite3

    sqlite3.ProgrammingError:提供的绑定数量不正确。当前语句使用1,并且提供了5个

  • mysql.连接器

    mysql.connector.errors.ProgrammingError: 1064 (42000): 您的 SQL 语法有错误;


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

  • 如果您切换连接器封装,则无需更改代码
  • 您可以保持查询参数一致​​的心理模型,即查询参数是一系列对象而不是单个对象。