【问题标题】:SQLite parameter substitution problemSQLite 参数替换问题
【发布时间】:2010-09-18 17:44:19
【问题描述】:

在 Python 2.5 中使用 SQLite3,我正在尝试遍历列表并根据项目的名称从数据库中提取项目的权重。

我尝试使用“?”建议使用参数替换来防止 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 个绑定。我该如何解决?

【问题讨论】:

  • 列数不是绑定数。查询中“?”的数量就是绑定的数量。
  • 是的,我知道。我只是认为代码以某种方式试图使用“create table”语句引用的绑定。我没有意识到它指的是项目本身的字母数。

标签: python sqlite


【解决方案1】:

Cursor.execute() 方法需要一个序列作为第二个参数。您提供的字符串恰好是 8 个字符长。

请改用以下表格:

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

Python 库参考:sqlite3 Cursor Objects.

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • 您可以执行以下操作:cursor.execute("SELECT * from %s WHERE name = ?" % table_name, (name,)),尽管这可能会使您的程序容易受到 SQL 注入攻击。
    • 对于用户输入决定表的情况,我从字典中提取表的名称,如果输入的内容是意外的,则会引发异常。可能不是 最好的 方法,但似乎不太容易导致 SQL 注入。
    • @James 我很好奇,在什么情况下您需要根据用户输入更改要插入的表?对于这种情况,使用 dict 听起来是个不错的主意,只要我使用 switch 语句,我就会使用它们。
    • @num1: 任何时候你想拥有一个将表名作为类 init() 参数的泛型类。因此,您不想硬编码表名,而是将其作为变量...
    • 您使用[] 表示表、字段名,? 表示值,
    【解决方案3】:

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

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

    第一个例子的结果是一个字符串;所以表示单值元组的正确方法是在第二次评估中。无论如何,下面的代码来修复你的错误。

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

    还将cursor.execute 值参数作为字符串提供(这就是您正在执行的操作)会导致示例中的第一次评估并导致您遇到错误。

    【讨论】:

    • @Kirk 但是参数替换对sqlite表名不起作用,所以上面那个是正确的。
    【解决方案4】:

    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查询中多次使用。

    【讨论】:

    【解决方案5】:

    你试过了吗? :

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

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

    【讨论】:

      【解决方案6】:

      引用(括号是什么意思?)? with parens 似乎对我有用。我一直在尝试(字面意思)'?但我一直得到

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

      当我这样做时:

      SELECT fact FROM factoids WHERE key LIKE (?)

      代替:

      SELECT fact FROM factoids WHERE key LIKE '?'

      成功了。

      这是一些 python 2.6 的东西吗?

      【讨论】:

      • 如果您使用 sqlite3 的参数替换(而不是 Python 的 %s 字符串插值),那么 ?根本不应该被引用,即SELECT fact FROM factoids WHERE key LIKE ?。只是碰巧在SQL中添加括号并不会改变含义,因此SELECT fact FROM factoids WHERE key LIKE (?)相当于没有()。
      • 如果您使用 Django 并且您的查询是 SELECT fact FROM factoids WHERE key LIKE "%s",那么您也需要避免使用引号。 cursor.execute() 不会将 %s 识别为该查询中的绑定。所以使用cursor.execute("Select fact FROM factoids WHERE key LIKE %s", (key_name, ))
      【解决方案7】:

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

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

      您应该执行以下操作:

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

      通过使用 (item, ) 你将它变成一个元组而不是一个字符串。

      【讨论】:

      • 这会产生很多查询。 N+1 问题。
      • 不知何故我有一个完整的编辑队列,所以我留下评论以进行编辑。 --- cursor.execute 函数接受 3 个参数:查询字符串、可迭代项列表、开关。这里可以使用“[item]”或“(item,)”来制作单个项目的项目列表——接受的答案只是另一种形式的解决方案,这是另一个@proofit404
      【解决方案8】:

      试试

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-21
        • 1970-01-01
        • 2010-11-06
        • 1970-01-01
        • 2021-07-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多