SQLite:将值列表绑定到“WHERE col IN (:PRM)”
Posted
技术标签:
【中文标题】SQLite:将值列表绑定到“WHERE col IN (:PRM)”【英文标题】:SQLite: bind list of values to "WHERE col IN ( :PRM )" 【发布时间】:2011-06-14 21:28:20 【问题描述】:我想要做的就是发送一个类似的查询
SELECT * FROM table WHERE col IN (110, 130, 90);
所以我准备了以下声明
SELECT * FROM table WHERE col IN (:LST);
然后我用
sqlite_bind_text(stmt, 1, "110, 130, 90", -1, SQLITE_STATIC);
不幸的是,这变成了
SELECT * FROM table WHERE col IN ('110, 130, 90');
并且是无用的(注意两个额外的单引号)。我已经尝试在字符串中添加额外的 ' 但它们被转义了。我没有找到关闭转义或防止文本被单引号括起来的选项。我能想到的最后一件事是不使用准备好的语句,但我只会把它作为最后的选择。您有什么想法或建议吗?
谢谢
编辑:
参数的数量是动态的,所以它可能是三个数字,如上例所示,一或十二。
【问题讨论】:
【参考方案1】:可以动态构建形式的参数化SQL语句
SELECT * FROM TABLE WHERE col IN (?, ?, ?)
然后为每个“?”调用一次 sqlite_bind_int您已添加到语句中。
无法将文本参数直接绑定到多个整数(或者,就此而言,多个文本)参数。
这是我想到的伪代码:
-- Args is an array of parameter values
for i = Lo(Args) to Hi(Args)
paramlist = paramlist + ', ?'
sql = 'SELECT * FROM TABLE WHERE col IN (' + Right(paramlist, 3) + ')'
for i = Lo(Args) to Hi(Args)
sql_bind_int(sql, i, Args[i]
-- execute query here.
【讨论】:
感谢您的回复。我在原始帖子中没有提到它(只是编辑过),但是参数的数量是动态的。我实际上仍然认为您的回答是“不可能”,这意味着我根本无法使用准备好的陈述。 不,Sebastian,我的建议是动态构建一个带有可变数量参数的 SQL 语句,然后在循环中为每个参数调用 sqlite_bind_in。它应该适合你。我将在答案中添加伪代码。 为了能够调用 sql_bind_* 需要先调用 sql_prepare。我没有看到动态构建语句,准备它,使用绑定,执行和终结的意义,而不是在构建查询时直接插入参数,准备,执行和终结。无论哪种方式,每次我想执行查询时我都需要调用 sql_prepare,这就是我想要保存并且只执行一次的操作。 不行,你肯定每次都要准备报表。如果不知道 IN () 子句中有多少值(该信息可能有助于执行计划),就无法准备语句。 我在尝试做同样的事情时遇到了这个问题。我认为该语言不支持它,至少作为扩展是非常可怕的。【参考方案2】:我自己刚遇到这个问题,但通过创建一个临时表并将所有值插入其中来回答它,以便我可以这样做:
SELECT * FROM TABLE WHERE col IN (SELECT col FROM temporarytable);
【讨论】:
你有性能对比吗?我可以想象创建临时表的开销超过了创建执行计划。但是,这将取决于表的大小。我想可以保留临时表并清除并每次重新使用它(假设正确同步)。仍然是填充 vs 执行计划 这是一个很好的解决方案。加快了表差异比较的代码顺序。【参考方案3】:更简单的是,像这样构建您的查询:
"SELECT * FROM TABLE WHERE col IN (" + ",".join(["?"] * len(lst)) + ")"
【讨论】:
那是什么语言?爪哇?! 它是 python。您在寻找什么语言? 这种解决方案应始终带有安全警告:如果有任何机会lst
不是绝对干净的数据(即它已被用户触摸...),那么系统可能容易受到此类方法的 SQL 注入攻击。
它还会在每次调用时强制重新编译 SQL。 也许系统会检测到它可以使用内部缓存......或者它可能不会,但你让事情变得困难。出于性能和安全原因,强烈推荐绑定参数。
@Stabledog:此示例使用准备好的语句。 SQL 仅取决于列表的长度,而不取决于内容。它可能是 DOS 向量,但不是任意 SQL 注入向量。【参考方案4】:
根据您的 sqlite 构建(它不是默认构建的一部分),您可以使用:
SELECT * FROM table WHERE col IN carray(?42);
然后使用(假设是 C API)绑定?42
:
int32_t data[] = 110, 130, 90;
sqlite3_carray_bind(
stmtPtr, 42,
data, sizeof(data)/sizeof(data[0]),
CARRAY_INT32, SQLITE_TRANSIENT);
我还没有实际测试过,我只是阅读了文档:https://sqlite.org/carray.html
【讨论】:
【参考方案5】:您不能将数组作为一个参数传递,但可以将每个数组值作为单独的参数传递 (IN (?, ?, ?)
)。
对动态数字参数执行此操作的安全方法(您不应使用字符串连接,.format()
等将值本身插入到查询中,这可能导致 SQL 注入)是生成查询字符串所需数量的?
占位符,然后绑定数组元素。如果您还需要传递其他参数,请使用数组连接或扩展语法(大多数语言中为*
或...
)。
以下是 Python 3 的示例:
c.execute('SELECT * FROM TABLE WHERE col IN () LIMIT ?'
.format(', '.join(['?'] * len(values))), [*values, limit])
【讨论】:
如果你有很多值,先把它们放到一个临时表中(在 SQLite 中驻留在内存中),然后在主查询中使用该表。【参考方案6】:处理相同的功能使我采用了这种方法: (nodejs, es6, Promise)
var deleteRecords = function (tblName, data)
return new Promise((resolve, reject) =>
var jdata = JSON.stringify(data);
this.run(`DELETE FROM $tblName WHERE id IN (?)`, jdata.substr(1, jdata.length - 2), function (err)
err ? reject('deleteRecords failed with : ' + err) : resolve();
);
);
;
【讨论】:
【参考方案7】:这也很好用(Javascript ES6):
let myList = [1, 2, 3];
`SELECT * FROM table WHERE col IN ($myList.join());`
【讨论】:
在运行时手动将数据注入查询是一个非常糟糕的主意。这是一个重大的安全风险,使您容易受到 SQL 注入攻击。 netsparker.com/blog/web-security/sql-injection-cheat-sheet【参考方案8】:一个更简单、更安全的答案只涉及生成掩码(与查询的数据部分相反)并允许 SQL 注入格式化程序引擎完成其工作。
假设我们在一个数组中有一些id
s,还有一些cb
回调:
/* we need to generate a '?' for each item in our mask */
const mask = Array(ids.length).fill('?').join();
db.get(`
SELECT *
FROM films f
WHERE f.id
IN ($mask)
`, ids, cb);
【讨论】:
【参考方案9】:你可以试试这个
RSQLite in R:
lst <- c("a", "b", "c")
dbGetQuery(db_con, paste0("SELECT * FROM table WHERE col IN (", paste0(shQuote(lst), collapse=", ") , ");"))
【讨论】:
【参考方案10】:我对节点的解决方案(ES6,Promises):
let records = await db.all(`
SELECT * FROM table
WHERE (column1 = ?) and column2 IN ( $[...val2s].fill('?').join(',') )
`, [val1, ...val2s])
适用于可变数量的可能值。
这使用sqlite-async,但你可以修改它为回调样式版本微不足道。
【讨论】:
【参考方案11】:例如,如果你想要sql查询:
select * from table where col in (110, 130, 90)
怎么样:
my_list = [110, 130, 90]
my_list_str = repr(my_list).replace('[','(').replace(']',')')
cur.execute("select * from table where col in %s" % my_list_str )
【讨论】:
以上是关于SQLite:将值列表绑定到“WHERE col IN (:PRM)”的主要内容,如果未能解决你的问题,请参考以下文章
如何将值存储在列表或数组中并将所有值绑定到数据表然后gridview