如何使用 Python 对 SQL IN 子句进行字符串格式化

Posted

技术标签:

【中文标题】如何使用 Python 对 SQL IN 子句进行字符串格式化【英文标题】:How to string format SQL IN clause with Python 【发布时间】: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 也有相似的看法 【参考方案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.

【讨论】:

这有点受限,因为它假定列表的长度,对吧?

以上是关于如何使用 Python 对 SQL IN 子句进行字符串格式化的主要内容,如果未能解决你的问题,请参考以下文章

sql面试题_SQl优化技巧_1注意通配符中like的使用,百分号放后面_2避免在where子句中对字段进行函数操作_3在子查询当中,尽量用exists代替in_4where子句中尽量不要使用(代码片

将值列表从 Python 传递到 SQL 查询的 IN 子句

PL/SQL - 如何在 IN 子句中使用数组

如何在 IN 子句 sql informix 中使用参数

如何解决大量数据的 IN 子句 SQL 查询中的性能问题?

如何在 Django 中为“in”SQL 子句传递值列表