SQLite参数替换问题


70

将SQLite3与Python 2.5结合使用,我试图遍历列表,并根据商品名称从数据库中获取商品的权重。

我尝试使用“?” 建议使用参数替换来防止SQL注入,但是它不起作用。例如,当我使用:

for item in self.inventory_names:
    self.cursor.execute("SELECT weight FROM Equipment WHERE name = ?", item)
    self.cursor.close()

我得到错误:

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

我认为这是由最初创建数据库引起的;我制作的实际上创建数据库的模块确实具有8个绑定。

cursor.execute("""CREATE TABLE Equipment 
    (id INTEGER PRIMARY KEY, 
    name TEXT,
    price INTEGER, 
    weight REAL, 
    info TEXT, 
    ammo_cap INTEGER, 
    availability_west TEXT,
    availability_east TEXT)""")

但是,当我为每个项目名称使用不太安全的“%s”替换时,它就可以正常工作。像这样:

for item in self.inventory_names:
    self.cursor.execute("SELECT weight FROM Equipment WHERE name = '%s'" % item)
    self.cursor.close()

我不知道为什么我只打一个电话时就认为我有8个bindin。我该如何解决?


列数不是绑定数。查询中“?”的数量是绑定的数量。
S.Lott

是的我知道。我只是认为代码以某种方式试图使用“创建表”语句所引用的绑定。我没有意识到它指的是项目本身中字母的数量。
crystalattice

Answers:


148

Cursor.execute()方法期望将序列作为第二参数。您提供的字符串恰巧是8个字符。

请使用以下形式:

self.cursor.execute("SELECT weight FROM Equipment WHERE name = ?", [item])

Python库参考:sqlite3游标对象


63

我花了半天的时间来弄清楚为什么这样的事情会给我一个错误:

cursor.execute("SELECT * from ? WHERE name = ?", (table_name, name))

只是发现表名称不能参数化。希望这可以帮助其他人节省一些时间。


3
您可以执行以下操作:cursor.execute("SELECT * from %s WHERE name = ?" % table_name, (name,)),尽管这可能会使您的程序容易受到SQL注入攻击。
2011年

6
对于用户输入确定表的情况,我将表名从dict中拉出,如果输入的内容意外,则会引发异常。可能不是执行此操作的最佳方法,但似乎不太可能导致SQL注入。
詹姆斯

@James我很好奇,在什么情况下需要根据用户输入更改要插入到哪个表?在这种情况下,使用dict听起来是一个不错的主意,每当我使用switch语句时,我都会使用它们。
NUM1

@ num1:任何时候您想要拥有一个以表名作为类init()参数的通用类。因此,您不想对表名进行硬编码,而是将其作为变量...
staggart

您使用[]表,字段名称和?值,
Pete Cacioppi 16'02-02

28

的参数cursor.execute表示您需要在数据库中插入的值应该是一个元组(序列)。但是,请考虑以下示例,看看发生了什么:

>>> ('jason')
'jason'

>>> ('jason',)
('jason',)

第一个示例取值为字符串。因此,表示单值元组的正确方法与第二次评估中相同。无论如何,下面的代码可以修复您的错误。

self.cursor.execute("SELECT weight FROM Equipment WHERE name = ?", (item,))

同样,将cursor.executevalue参数作为字符串给出(这是您正在做的),这将导致示例中的第一次评估,并导致出现错误。


@Kirk,但参数替换不适用于sqlite表名,因此上面的一个是正确的。
rjurney19年

5

sqlite3模块支持两种类型的参数占位符:

qmark风格

使用一个或多个?标记每个参数的位置,并提供参数列表或元组。例如:

curs.execute("SELECT weight FROM Equipment WHERE name = ? AND price = ?",
             ['lead', 24])

命名样式

:par为每个命名参数使用占位符,并提供一个字典。例如:

curs.execute("SELECT weight FROM Equipment WHERE name = :name AND price = :price",
             {name: 'lead', price: 24})

命名样式参数的优点是您不必担心参数的顺序,并且每个参数:par在大型/复杂SQL查询中可以多次使用。


不同的paramstyle选项怎么样?sqlite3支持哪一种,写在哪里?python.org/dev/peps/pep-0249/#paramstyle
弗拉基米尔·

2

你尝试过这个吗?:

for item in self.inventory_names:
    t = (item,)
    self.cursor.execute("SELECT weight FROM Equipment WHERE name = ?", t)
    self.cursor.close()

cursor.execute()需要一个序列(列表,元组)作为第二个参数。(-> ddaa)


1

引用(这是括号的意思吗?)?与parens似乎为我工作。我一直在尝试(字面上)“?” 但我不断

ProgrammingError:提供的绑定数不正确。当前语句使用0,并且提供了1。

当我这样做时:

从事实中选择事实WHERE键,如(?)

代替:

从事实中选择事实WHERE键,例如“?”

有效。

这是python 2.6的东西吗?


1
如果您使用sqlite3的参数替换(而不是Python的%s字符串插值),则使用?完全不应该被引用,即SELECT fact FROM factoids WHERE key LIKE ?。只是碰巧在SQL中添加括号不会改变含义,因此SELECT fact FROM factoids WHERE key LIKE (?)等同于不带()。
Lie Ryan

如果您使用的是Django,而您的查询是,SELECT fact FROM factoids WHERE key LIKE "%s"则还需要避免使用引号。cursor.execute()不会将%s识别为该查询中的绑定。所以使用cursor.execute("Select fact FROM factoids WHERE key LIKE %s", (key_name, ))
MrOodles

0

项目的每个元素必须是一个元组。假设名称看起来像这样:

names = ['Joe', 'Bob', 'Mary']

您应该执行以下操作:

for item in self.inventory_names:
self.cursor.execute("SELECT weight FROM Equipment WHERE name = ?", (item, ))

通过使用(item,),您使其成为一个元组而不是字符串。


这会产生很多查询。N + 1问题。
proofit404

我以某种方式拥有完整的编辑队列,因此我要发表评论以进行编辑。--- cursor.execute函数接受3个参数:查询字符串,可迭代的项目列表,一个开关。这里可使用“[项目]”或“(项目)” ---接受回答是的溶液只是另一种形式,而这是其它@ proofit404制成单个项目项目列表要么
耶尔马兹杜尔马兹

-6

尝试

execute("select fact from factoids where key like ?", "%%s%" % val)

您根本不需要包装任何东西?,Python sqlite会正确地将其转换为带引号的实体。

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.