【问题标题】:How to loop through multi-line sql query and use them in Knex Transactions?如何循环遍历多行 sql 查询并在 Knex Transactions 中使用它们?
【发布时间】:2019-04-09 01:45:25
【问题描述】:

Node 和 Knex 的新手来自 RoR 背景,我需要将特定函数转换为 Node.js

这是我的 RoR 代码:

  def self.save!
    update_queries = self.fetch.map{|currency, rate|
      "UPDATE currency_exchange_rates SET rate='#{rate}' WHERE currency='#{currency}'"
    }.join(';')

    ActiveRecord::Base.connection.execute(update_queries)
  end

到目前为止,这是我在 Nodejs 中所拥有的:

static save(new_rates){
      return new Promise(async (resolve, reject) => {
        var query = Object.keys(rate).map(key => 'UPDATE currency_exchange_rates SET rate=' + rate[key] + ' WHERE currency = "' + key + '"').join('; ');
    })
  }

输入数据如下所示:

新价格 =

'UPDATE currency_exchange_rates SET rate=32102793.12 WHERE currency= "IDR"; 

UPDATE currency_exchange_rates SET rate=0.7822 WHERE currency= "USDGBP"; 

UPDATE currency_exchange_rates SET rate=3189.756317 WHERE currency= "CAD"; 

UPDATE currency_exchange_rates SET rate=152.8 WHERE currency= "USDLKR"; 

UPDATE currency_exchange_rates SET rate=110.818 WHERE currency= "USDJPY"; 

UPDATE currency_exchange_rates SET rate=1.3103 WHERE currency= "USDAUD"'

我正在使用Knex.js 连接到我的 pg 数据库。如何使用 knex 执行所有这些操作?我可以通过knex.raw() 中的所有查询吗? knex.transaction() 怎么样?它们有什么区别?

提前致谢!

【问题讨论】:

  • 你可以使用bluebirdmapSeries
  • 哦,谢谢,我会检查一下

标签: node.js knex.js


【解决方案1】:
// [Sequential Update] Commit transaction after each successfully executed query
await knex.transaction(async trx => {
    return Promise.all(Object.keys(rate).map(key => trx('currency_exchange_rates')
            .update({ rate: rate[key] })
            .where({ currency: key })
    ));
});

// [Bulk Update] Commit transaction after all queries have been executed successfully
await knex.transaction(trx => {
    return Promise.all(Object.keys(rate).map(key => knex('currency_exchange_rates')
            .update({ rate: rate[key] })
            .where({ currency: key })
        ))
        .then(trx.commit)
        .catch(trx.rollback);
});

【讨论】:

  • 如果有大量数据需要更新,这种为每个查询并行创建查询构建器的方式将非常低效地使用内存。所有查询构建器都是并行创建的,它们都被并行发送到驱动程序,然后驱动程序只是将它们全部缓冲,并且仍将通过事务的单个连接顺序运行这些查询。虽然通常只更新少量数据并不重要。
  • @MikaelLepistö 感谢您的意见,正在阅读您的两个答案之间的差异。为了简单起见,我确实使用了你的,但这个回复更好地解释了它的区别。再次感谢!
【解决方案2】:

这样的做法很巧妙:

let retVal = await knex.transaction(async (trx) => {
  for (let key of Object.keys(rate)) {
    let value = rate[key];
    await trx('currency_exchange_rates')
      .update({ rate: value }).where('currency', key);
  }
  return "woo done"; // this value is returned from transaction 
});

在处理事务时,通常希望在通过事务的单个 DB 连接运行查询之前,一个一个地创建和运行查询,以避免不必要的查询缓冲。

【讨论】:

  • 感谢脚注说明我为什么要逐个运行查询。我也一直在尝试理解 .transactions !我不太明白当 .update 已经可以完成这项工作时,为什么仍然需要 .transaction ?谢谢!
  • @Reiion 请从任何 SQL 教程或维基百科等中查看事务的含义以及使用它们的原因。基本上它允许将所有更新作为原子操作进行,其中所有更新都提交到数据库,或者即使一个失败,所有更新都将被拒绝。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-08
  • 2011-03-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多