【问题标题】:SQLite: bind list of values to "WHERE col IN ( :PRM )"SQLite:将值列表绑定到“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');

并且是无用的(注意两个额外的单引号)。我已经尝试在字符串中添加额外的 ' 但它们被转义了。我没有找到关闭转义或防止文本被单引号括起来的选项。我能想到的最后一件事是不使用准备好的语句,但我只会把它作为最后的选择。您有什么想法或建议吗?

谢谢

编辑:

参数的数量是动态的,所以它可能是三个数字,如上例所示,一或十二。

【问题讨论】:

    标签: sqlite select prepared-statement bind


    【解决方案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()});`
        

        【讨论】:

        【解决方案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 )
              

              【讨论】:

                猜你喜欢
                • 2021-12-04
                • 2015-01-06
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2010-11-07
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多