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 注入格式化程序引擎完成其工作。

假设我们在一个数组中有一些ids,还有一些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

QtSql:绑定不会改变使用 SQLite 的查询

在 Windows 窗体应用程序 C# 中以编辑模式将值绑定到组合框

将值和数组添加到 PDO 执行方法