SQLite“IN”子句的参数replace

我想在SQL语句中使用SQLite中的参数replaceIN子句。 这是一个完整的运行示例,演示:

import sqlite3 c = sqlite3.connect(":memory:") c.execute('CREATE TABLE distro (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)') for name in 'Ubuntu Fedora Puppy DSL SuSE'.split(): c.execute('INSERT INTO distro (name) VALUES (?)', [ name ] ) desired_ids = ["1", "2", "5", "47"] result_set = c.execute('SELECT * FROM distro WHERE id IN (%s)' % (", ".join(desired_ids)), ()) for result in result_set: print result 

它打印出来:

(1,u'Ubuntu')(2,u'Fedora')(5,u'SuSE')

正如文档所说:“你不应该使用Python的string操作来组装你的查询,因为这样做是不安全的;它会使你的程序容易受到SQL注入攻击,”我希望能够使用参数replace。

当我尝试:

 result_set = c.execute('SELECT * FROM distro WHERE id IN (?)', [ (", ".join(desired_ids)) ]) 

我得到一个空的结果集,当我尝试:

 result_set = c.execute('SELECT * FROM distro WHERE id IN (?)', [ desired_ids ] ) 

我得到:

InterfaceError:错误绑定参数0 – 可能不支持的types。

虽然我希望对这个简化的问题的任何答案都能工作,但我想指出,我想要执行的实际查询是在一个双重嵌套的子查询中。 以机智:

 UPDATE dir_x_user SET user_revision = user_attempted_revision WHERE user_id IN (SELECT user_id FROM (SELECT user_id, MAX(revision) FROM users WHERE obfuscated_name IN ("Argl883", "Manf496", "Mook657") GROUP BY user_id ) ) 

你需要正确的数字? s,但是这不会造成sql注入风险:

 >>> result_set = c.execute('SELECT * FROM distro WHERE id IN (%s)' % ','.join('?'*len(desired_ids)), desired_ids) >>> print result_set.fetchall() [(1, u'Ubuntu'), (2, u'Fedora'), (5, u'SuSE')] 

根据http://www.sqlite.org/limits.html (项目9),SQLite不能(默认情况下)处理超过999个参数的查询,所以这里的解决scheme(生成所需的占位符列表)将如果你有成千上万的项目,你正在寻找失败。 如果是这样的话,你将需要分解清单,然后循环部分,并自己join结果。

如果你的IN子句中不需要数以千计的项目,那么Alex的解决scheme就是这样做的(看起来Django是这么做的)。

更新:这工作:

 import sqlite3 c = sqlite3.connect(":memory:") c.execute('CREATE TABLE distro (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)') for name in 'Ubuntu Fedora Puppy DSL SuSE'.split(): c.execute('INSERT INTO distro (name) VALUES (?)', ( name,) ) desired_ids = ["1", "2", "5", "47"] result_set = c.execute('SELECT * FROM distro WHERE id IN (%s)' % ("?," * len(desired_ids))[:-1], desired_ids) for result in result_set: print result 

问题是你需要有一个? 为input列表中的每个元素。

语句("?," * len(desired_ids))[:-1]将重复string“?”,然后切断最后一个逗号。 因此在desired_ids中每个元素都有一个问号。

我总是最终做这样的事情:

 query = 'SELECT * FROM distro WHERE id IN (%s)' % ','.join('?' for i in desired_ids) c.execute(query, desired_ids) 

没有注入风险,因为你没有把desired_ids中的string直接放到查询中。

如果sqlite与sql请求的长度有问题,不定数量的问号可以是某种方式来啄东西。