在knex中批量更新

Nik*_*des 9 javascript knex.js

我想使用Knex.js执行批量更新

例如:

'UPDATE foo SET [theValues] WHERE idFoo = 1'
'UPDATE foo SET [theValues] WHERE idFoo = 2'
Run Code Online (Sandbox Code Playgroud)

值:

{ name: "FooName1", checked: true } // to `idFoo = 1`
{ name: "FooName2", checked: false } // to `idFoo = 2`
Run Code Online (Sandbox Code Playgroud)

我之前使用的是node-mysql,它允许多个语句.使用它时我只是构建了一个多语句查询字符串,只需在一次运行中通过线路发送.

我不确定如何用Knex实现同样的目标.我可以batchInsert看作我可以使用的API方法,但就任何batchUpdate问题而言都没有.

注意:

  • 我可以进行异步迭代并分别更新每一行.这是不好的原因,这意味着从服务器到数据库将会有很多往返

  • 我可以使用raw()Knex 的东西,可能做类似于我对node-mysql的操作.然而,这破坏了作为DB抽象层的整个knex目的(它引入了强大的DB耦合)

所以我想用"knex-y"来做这件事.

欢迎任何想法.

小智 12

我需要在事务中执行批量更新(我不想在出现问题时进行部分更新).我已经通过下一个方式解决了这个问题:

// I wrap knex as 'connection'
return connection.transaction(trx => {
    const queries = [];
    users.forEach(user => {
        const query = connection('users')
            .where('id', user.id)
            .update({
                lastActivity: user.lastActivity,
                points: user.points,
            })
            .transacting(trx); // This makes every update be in the same transaction
        queries.push(query);
    });

    Promise.all(queries) // Once every query is written
        .then(trx.commit) // We try to execute all of them
        .catch(trx.rollback); // And rollback in case any of them goes wrong
});
Run Code Online (Sandbox Code Playgroud)

  • 使用 mssql 时,如果尝试 Promise.all(queries),tedious(NodeJS mssql 库)将出错并显示“无法获取请求的连接”。还有另一个请求正在进行中。`,Knex 随后显示​​“无法回滚事务”。有一个请求正在进行中。` 这是因为 Promise.all() 并行执行所有查询。为了解决这个问题,所有的承诺都必须一个接一个地得到解决。您可以使用[在此 stackoverflow 问题中](/sf/ask/1721027731/) 中的代码来执行此操作。 (2认同)

Pat*_*ard 11

您很清楚每种方法的优缺点.我建议一个原始查询,批量更新多个异步更新.是的,您可以并行运行它们,但您的瓶颈将成为数据库运行每次更新所需的时间.细节可以在这里找到.

下面是使用knex.raw进行批量upsert的示例.假设记录是一个对象数组(我们想要更新的每一行的一个obj),其值是与要更新的数据库中的列对齐的属性名称:

var knex = require('knex'),
    _ = require('underscore');

function bulkUpdate (records) {
      var updateQuery = [
          'INSERT INTO mytable (primaryKeyCol, col2, colN) VALUES',
          _.map(records, () => '(?)').join(','),
          'ON DUPLICATE KEY UPDATE',
          'col2 = VALUES(col2),',
          'colN = VALUES(colN)'
      ].join(' '),

      vals = [];

      _(records).map(record => {
          vals.push(_(record).values());
      });

      return knex.raw(updateQuery, vals);
 }
Run Code Online (Sandbox Code Playgroud)

这个答案很好地解释了两种方法之间的运行时关系.

编辑:

要求我展示records这个例子中的样子.

var records = [
  { primaryKeyCol: 123, col2: 'foo', colN: 'bar' },
  { // some other record, same props }
];
Run Code Online (Sandbox Code Playgroud)

请注意,如果您的record附加属性不是您在查询中指定的属性,则不能执行以下操作:

  _(records).map(record => {
      vals.push(_(record).values());
  });
Run Code Online (Sandbox Code Playgroud)

因为您将为每个记录的查询分配太多值,并且knex将无法使每个记录的属性值与?查询中的字符匹配.您需要显式地将要插入的每个记录的值推送到数组中,如下所示:

  // assume a record has additional property `type` that you dont want to
  // insert into the database
  // example: { primaryKeyCol: 123, col2: 'foo', colN: 'bar', type: 'baz' }
  _(records).map(record => {
      vals.push(record.primaryKeyCol);
      vals.push(record.col2);
      vals.push(record.colN);
  });
Run Code Online (Sandbox Code Playgroud)

执行上述显式引用的重复方式较少,但这只是一个示例.希望这可以帮助!


jim*_*kiz 6

假设您有给定表的有效键/值的集合:

// abstract transactional batch update
function batchUpdate(table, collection) {
  return knex.transaction(trx => {
    const queries = collection.map(tuple =>
      knex(table)
        .where('id', tuple.id)
        .update(tuple)
        .transacting(trx)
    );
    return Promise.all(queries)
      .then(trx.commit)    
      .catch(trx.rollback);
  });
}
Run Code Online (Sandbox Code Playgroud)

称呼它

batchUpdate('user', [...]);
Run Code Online (Sandbox Code Playgroud)

不幸的是,您是否受制于非常规列名?不用担心,我让你成全了:

function batchUpdate(options, collection) {
  return knex.transaction((trx) => {
    const queries = collection.map(tuple =>
      knex(options.table)
        .where(options.column, tuple[options.column])
        .update(tuple)
        .transacting(trx)
    );
    return Promise.all(queries)
      .then(trx.commit)    
      .catch(trx.rollback);
  });
}
Run Code Online (Sandbox Code Playgroud)

称呼它

batchUpdate({ table: 'user', column: 'user_id' }, [...]);
Run Code Online (Sandbox Code Playgroud)