【问题标题】:How to use variables in SQL statement in Python?如何在 Python 的 SQL 语句中使用变量?
【发布时间】:2010-10-28 11:51:29
【问题描述】:

我有以下 Python 代码:

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

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

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

【问题讨论】:

    标签: python sql


    【解决方案1】:

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

    给定查询

    INSERT INTO mytable (fruit) VALUES (%s)
    

    通常*,传递给cursor.execute 的值必须仍然是tuple,即使它是单例,所以我们必须提供一个单元素元组,例如:(value,)

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

    传递单个字符串

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

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

    • psycopg2:

      TypeError:字符串格式化期间并非所有参数都转换

    • sqlite3

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

    • mysql.connector

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


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

    • 切换连接器包无需更改代码
    • 您保持查询参数的一致心智模型是一系列对象而不是单个对象。

    【讨论】:

      【解决方案2】:

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

      当您只是将变量的值附加到您的语句时要小心: 想象一个用户自称';DROP TABLE Users;'—— 这就是为什么您需要使用 SQL 转义,当您以体面的方式使用 cursor.execute 时,Python 会为您提供这种转义。 URL 中的示例是:

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

      【讨论】:

      • 其实并不是SQL转义。它是变量绑定,更简单、更直接。解析后将值绑定到 SQL 语句中,使其免受任何注入攻击。
      • 好吧,是 SQL 转义还是变量绑定取决于您的数据库服务器/DB-API 驱动程序的好坏。我见过一些现实世界中广泛部署的生产数据库,它们的 DB-API 驱动程序只是进行转义,而不是将数据和代码带外保存在网络上。不用说,我不太尊重那些所谓的“数据库”。
      【解决方案3】:

      很多方法。 不要在实际代码中使用最明显的一个(%s%),它对attacks 开放。

      这里复制粘贴from pydoc of sqlite3

      # 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))
      

      【讨论】:

      • 一些 DB-API 实现实际上使用 %s 作为其变量——最值得注意的是用于 PostgreSQL 的 psycopg2。不要将这与使用 %s 和 % 运算符进行字符串替换混淆(尽管很容易混淆)。如果为了可移植性,我们可以有一个定义的标准方法来为 DB-API 指定 SQL 参数,我会非常高兴。
      【解决方案4】:
      cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))
      

      注意参数是作为元组传递的。

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

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

      【讨论】:

      • 有趣,为什么它单独使用变量而不是数组(var1,var2,var3)?
      • 根据 DB API 规范,它看起来可以是任何一种方式:python.org/dev/peps/pep-0249
      • @thekashyap 再次仔细阅读。不安全的是使用字符串格式化运算符%。其实我在回答中也是这么说的。
      • 投反对票,因为答案说不使用%,但使用了三遍。更多解释会很棒。
      • @eric 答案说不要使用% operator 来格式化字符串。字符串中的那些%cursor.execute 直接使用,因为它知道它正在生成SQL,它可以做更多的事情来保护你。
      【解决方案5】:

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

      【讨论】:

      • 是否可以使用外部 SQL 脚本做同样的事情?
      猜你喜欢
      • 2022-12-21
      相关资源
      最近更新 更多