如何使用 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 时,为什么还要执行原始 sqlIN
查询?
@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子句中尽量不要使用(代码片