【问题标题】:How to string format SQL IN clause with Python如何使用 Python 对 SQL IN 子句进行字符串格式化
【发布时间】:2012-06-27 02:26:12
【问题描述】:

我正在尝试创建如下语句:

SELECT * FROM table WHERE provider IN ('provider1', 'provider2', ...)

但是,我在 Django API 的字符串格式方面遇到了一些问题。到目前为止,这是我所拥有的:

profile = request.user.get_profile()
providers = profile.provider.values_list('provider', flat=True) # [u'provider1', u'provider2']
providers = tuple[str(item) for item in providers] # ('provider1', 'provider2')

SQL = "SELECT * FROM table WHERE provider IN %s"
args = (providers,)
cursor.execute(sql,args)

DatabaseError
(1241, 'Operand should contain 1 column(s)')

【问题讨论】:

  • 很好奇。当您已经拥有 django ORM 时,为什么还要执行原始 sql IN 查询?
  • @jdi 这是一个很长的 sql 查询,我正在根据一些用户输入的值(大约 20 行)进行字符串连接。
  • ORM 有聚合。但我想我只需要相信你的话,ORM 做不到 :-)
  • 我在这里与@jdi 在一起-您要在ORM 中查询的表是什么? (稍作修改:我不是 with jdi,我的意思是有点混乱的观点......)
  • @JonClements:乔恩,我被冒犯了。我们谈到在公共场合要诚实。大卫,我们在一起,我们对数据库 ORM 也有相似的看法

标签: python mysql sql django


【解决方案1】:

我不特别喜欢的另一个答案,但适用于您明显的用例:

providers = tuple[str(item) for item in providers] # ('provider1', 'provider2')
# rest of stuff...

SQL = 'SELECT * FROM table WHERE provider IN {}'.format(repr(providers))
cursor.execute(SQL)

【讨论】:

  • 也可以写成 '...{!r}.format(providers) 我猜 - 只是取决于口味
  • 这个选项不好,因为如果列表中只有 1i 项,最后会有一个逗号,这是无效的语法
【解决方案2】:

试试这个....应该可以的。

SQL = "SELECT * FROM table WHERE provider IN %s"%(providers)
exec 'cursor.execute("%s")'%(SQL)

【讨论】:

    【解决方案3】:

    所以,您需要输入 ID 的字符串:

    some_vals = '1 3 5 76 5 4 2 5 7 8'.split() # convert to suitable type if required
    SomeModel.objects.filter(provider__in=some_vals)
    

    【讨论】:

    • 是的,我在主要的 cmets 中询问过这个问题。 OP 说他们进行原始查询是有特定原因的
    • @jdi 有趣 - 如果对象在 ORM 内,则可以从此答案中检索 SQL 查询。否则,我只能认为他们在查询 ORM 之外的内容。
    • 我完全同意你的看法,当然。我们只能假设 OP 有 ORM 根本无法满足的需求。
    【解决方案4】:

    MySQLdb 有一个方法可以帮助解决这个问题:

    文档

    string_literal(...) string_literal(obj) -- 将对象 obj 转换为 SQL 字符串文字。 这意味着,任何特殊的 SQL 字符都会被转义,并且被括起来 在单引号内。换句话说,它执行:

    "'%s'" % escape_string(str(obj))
    
    Use connection.string_literal(obj), if you use it at all.
    _mysql.string_literal(obj) cannot handle character sets.
    

    用法

    # connection:  <_mysql.connection open to 'localhost' at 1008b2420>
    
    str_value = connection.string_literal(tuple(provider))
    # '(\'provider1\', \'provider2\')'
    
    SQL = "SELECT * FROM table WHERE provider IN %s"
    args = (str_value,)
    cursor.execute(sql,args) 
    

    【讨论】:

    • 这很酷。不知道 string_literal 方法。我假设由于 MySQLdb 非常严格地遵循 db api,这存在于大多数实现中?(sqlite3、postgres 等)明确地回到文档
    • @Justin.Wood:说实话,我也不是很了解它,但是我以前没有太多使用 MySQLdb 和传递元组值的经验。它始终是一个 ORM。我也只是查看了这个文档:-)
    • 整洁。我在一些不使用 ORM 的遗留代码库中工作,我们不断地得到这种东西。所以这对我来说将是一个很大的优势。您在文档中有指向此的链接吗?找不到它。虽然在源代码中很容易找到。
    • @Justin.Wood:奇怪的是,在某些版本中它被简单地称为literal:mysql-python.sourceforge.net/MySQLdb-1.2.2/public/…
    • 不,这是 MySQLdb 的内部函数。您应该使用答案中使用的语法。它在源卢克! ` def _get_string_literal(): def string_literal(obj, dummy=None): return db.string_literal(obj) return string_literal def _get_unicode_literal(): def unicode_literal(u, dummy=None): return db.literal(u.encode(unicode_literal) .charset)) 返回 unicode_literal `
    【解决方案5】:

    在将字符串传递给游标对象执行之前,您可能应该进行字符串替换:

    sql = "SELECT * FROM table WHERE provider IN (%s)" % \
            (','.join(str(x) for x in providers))
    cursor.execute(sql)
    

    【讨论】:

    • 这可能有问题,因为字符串没有被引用。也许用repr替换str
    • 还可以在连接中添加引号,例如。 %('"'+'","'.join(str(x) for x in providers)+'"') - 虽然我想这可能被视为有点 hack
    • 但我认为这个解决方案需要它。
    • FWIW,这种方法容易受到SQL注入攻击。接受的答案(由 jdi 提供)更安全。
    【解决方案6】:
    "SELECT * FROM table WHERE provider IN ({0},{1},{2})".format(*args) #where args is list or tuple of arguments.
    

    【讨论】:

    • 这有点受限,因为它假定列表的长度,对吧?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-09
    • 2013-08-06
    • 1970-01-01
    • 1970-01-01
    • 2011-07-11
    相关资源
    最近更新 更多