【问题标题】:JavaScript nodejs mysql with queries in a loopJavaScript nodejs mysql,循环查询
【发布时间】:2022-01-31 04:25:50
【问题描述】:

我现在感觉有点傻。我对nodejs和javaScript相当陌生,无法弄清楚这一点。我想这是因为对 mysql 的查询的异步性质......

我做了一个例子来说明我的问题。我只想循环处理一些 sql 查询并对结果进行处理。为了这个例子,我只是打印出一些东西。我知道我可以使用像 "SELECT id, name FROM player WHERE id IN (1,2,3,4,5)" 这样的单个 sql 查询,但这在我尝试编写的实际应用程序中是不可能的。

这是我的 nodejs app.js 的相关部分

var mysql = require("mysql");
var mysqlPool = mysql.createPool(conf.mysqlArbData);

for (var i = 0; i<5; i++){

    mysqlPool.getConnection(function(err, connection) {

        var detailSql = "SELECT id, name FROM player " +
            "WHERE id = "+i;
        if (err){
            throw err;
        }
        connection.query(detailSql, function(err, detailRows, fields) {
            connection.end();
            console.log("detailSql="+detailSql);
            if (err){
                console.log("can't run query=" + detailSql +"\n Error="+err);
            }
            else{

                console.log(detailRows[0].id + " " +detailRows[0].name);

            }

        });
    });

};

现在输出:

web server listening on port 3000 in development mode
detailSql=SELECT id, name FROM player WHERE id = 5
5 Jyvaskyla
detailSql=SELECT id, name FROM player WHERE id = 5
5 Jyvaskyla
detailSql=SELECT id, name FROM player WHERE id = 5
5 Jyvaskyla
detailSql=SELECT id, name FROM player WHERE id = 5
5 Jyvaskyla
detailSql=SELECT id, name FROM player WHERE id = 5
5 Jyvaskyla

我的问题是,为什么我只能得到 id=5 的数据库条目的结果?为了在回调中接收每个单独的结果需要进行哪些更改?

【问题讨论】:

    标签: javascript mysql node.js


    【解决方案1】:

    问题在于getConnection 是异步的并且 Javascript 没有块作用域,这意味着在调用getConnection 的回调时,i 变量将指向它在循环中最后的值(即 5)。

    您可以使用技巧为循环的每一轮创建一个偏函数(将其视为已应用第一个参数的函数),它将i 的当前值作为第一个参数传递getConnection 回调:

    for (var i = 0; i<5; i++) {
      mysqlPool.getConnection(function(i, err, connection) {
        ...
      }.bind(mysqlPool, i));
    };
    

    FWIW,您的代码几乎会立即打开与您的数据库的 5 个连接(并执行 5 个查询)(这就是异步 I/O 的工作方式)。这可能不是什么大问题,但如果5 可以变得更高,那就值得实现了:)

    此外,for 循环将生成 [0, 1, 2, 3, 4],而在您的示例查询中,您编写的是 WHERE id IN (1, 2, 3, 4, 5)

    【讨论】:

    • 谢谢。我会尽快尝试您建议的解决方案。关于并发查询的提示很有帮助,但我认为连接池初始化时最多有 10 个并发连接。我希望有更多的人排队。最后,从0开始的索引没有问题,只是我的一个错误,我应该写[0,1,2,3,4]
    • 绑定多个参数呢?
    • @GameDevGuru function(arg1, arg2, err, connection) { ... }.bind(mysqlPool, i, j)
    • @robertklep 我采用了数组方法。对我来说看起来和感觉更干净
    【解决方案2】:

    对于节点,您可以使用let 语句。它限制了 for 循环中的 i 范围。

    for (let i = 0; i<5; i++) {
      mysqlPool.getConnection(function(err, connection) {
        console.log(i);
      });
    };
    

    【讨论】:

      【解决方案3】:

      这就是我一直以来的做法。它适用于 UPDATEINSERT 具有 50,000 多个项目的语句。

      const mysql = mysql.createConnection({
        host     :   'HOST',
        user     :   'USER',
        password :   'PASSWORD',
        database :   'DATABASE_NAME',
        port     :   PORT,
        ssl      :   { ca : fs.readFileSync(process.cwd() + '/ssl/certs/ca-certificate.crt') },
      });
      
      
      async function processRequest(itemArray) {
      
        //pre-processing of array (if needed...)
      
        // initialize sql connection
        mysql.connect();
      
        const startProcess = await processItems(itemArray)
          .catch(e => {
            // end sql connection if there was an error running startProcess
            mysql.end();
            throw new Error(`Error @ startProcess: ${e}`);
          })
      
        // end sql connection after startProcess is finished
        mysql.end();
        
        // optional return of startProcess. This occurs after processItems is done.
        try {
        
          if (startProcess.status == 'success') {
            return {status: 'success', msg: 'function startProcess has compelted.';
          }
      
        }
        
        catch (e) {
            return {status: 'error', msg: 'function startProcess did not complete.', error: e;
        }
      
      
        function processItems(array){
      
          return new Promise((resolve,reject) => {
      
            for (let i in array) {
      
              // perform DB operations inside loop
              mysql.query(`UPDATE db SET ?`, array, (err, results, fields) => {
                // exit if there's any sort of error
                if (err) reject(err)
              })
      
            }
      
          // resolve promise after loop completes
          resolve({status: 'success'});
      
          })
      
        };
      
      }

      工作流程如下:

      1. mysql 连接打开(通过mysql.connect()
      2. processRequest 被调用(在我的例子中,它是从 Route 调用的)
      3. processItems 函数被称为asynchronously
      4. processItems 返回 promise
      5. processItems 完成了它解决或拒绝它的地方
      6. mysql 连接已关闭(通过mysql.end()

      【讨论】:

        猜你喜欢
        • 2021-06-08
        • 1970-01-01
        • 1970-01-01
        • 2020-08-20
        • 2012-05-09
        • 2018-05-15
        • 2016-10-16
        • 2015-02-15
        • 2021-03-19
        相关资源
        最近更新 更多