在knex中批量更新

时间:2016-11-11 08:08:33

标签: javascript knex.js

我想使用Knex.js

执行批量更新

例如:

'UPDATE foo SET [theValues] WHERE idFoo = 1'
'UPDATE foo SET [theValues] WHERE idFoo = 2'

值:

{ name: "FooName1", checked: true } // to `idFoo = 1`
{ name: "FooName2", checked: false } // to `idFoo = 2`

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

我不确定如何使用Knex实现相同目的。我可以看到batchInsert作为我可以使用的API方法,但就batchUpdate而言并非如此。

注意:

  • 我可以进行异步迭代并分别更新每一行。这很糟糕,因为这意味着从服务器到数据库的往返很多

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

所以我想用“knex-y”来做这件事。

欢迎任何想法。

4 个答案:

答案 0 :(得分: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
});

答案 1 :(得分:10)

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

下面是使用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);
 }

This回答很好地解释了两种方法之间的运行时关系。

编辑:

要求我在此示例中显示records的外观。

var records = [
  { primaryKeyCol: 123, col2: 'foo', colN: 'bar' },
  { // some other record, same props }
];

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

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

因为您将为每个记录的查询分配太多值,并且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);
  });

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

答案 2 :(得分: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);
  });
}

称呼它

batchUpdate('user', [...]);

您是否不幸受到非常规列名的约束?不用担心,我让你成全了

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);
  });
}

称呼它

batchUpdate({ table: 'user', column: 'user_id' }, [...]);

答案 3 :(得分:0)

该解决方案非常适合我!我只包含一个ID参数,以使其在具有自定义ID标签的表之间动态变化。 Chenhai,这是我的摘录,其中包括一种返回交易的ID值的单个数组的方法:

function batchUpdate(table, id, collection) {
   return knex.transaction((trx) => {
       const queries = collection.map(async (tuple) => {
       const [tupleId] = await knex(table)
        .where(`${id}`, tuple[id])
        .update(tuple)
        .transacting(trx)
        .returning(id);

       return tupleId;
   });

   return Promise.all(queries).then(trx.commit).catch(trx.rollback);
   });
}

您可以使用 response = await batchUpdate("table_name", "custom_table_id", [array of rows to update]) 获取返回的ID数组。