Nodejs续集大量upsert

Ash*_*osh 21 node.js sequelize.js

有没有办法在sequelize中进行批量upsert.另外,我可以指定用于检查重复的密钥.

我试过以下但没有奏效:

Employee.bulkCreate(data, {
    updateOnDuplicate: true
});
Run Code Online (Sandbox Code Playgroud)

批量创作工作正常.上面的语句总是在DB中创建新条目.

小智 36

来自官方sequelizejs参考.

这是可以做到用bulkCreateupdateOnDuplicate选项.

像这样例如:

Employee.bulkCreate(dataArray, 
    {
        fields:["id", "name", "address"] ,
        updateOnDuplicate: ["name"] 
    } )
Run Code Online (Sandbox Code Playgroud)

updateOnDuplicate是一个字段数组,当主键(或可能是唯一键)与行匹配时将更新这些字段.确保模型中至少有一个唯一的字段(比如说id),并且dataArray两者都有upsert.

  • 不幸的是,文档说只有mysql =支持这个选项( (24认同)
  • 现在Postgres也支持-“如果行键已经存在(在重复键更新时)要更新的字段?(仅MySQL,MariaDB和Postgres> = 9.5支持。默认情况下,所有字段都被更新。” (5认同)
  • 值得一提的是:字段“updatedAt”通常由sequelize自动更新,除非通过“updateOnDuplicate”显式传递,否则不会更新。 (3认同)
  • 注意:如果您的表具有 uniq 索引,则此解决方案将不起作用。PR 正在进行中:https://github.com/sequelize/sequelize/pull/12516 (2认同)

Can*_*Can 9

由于答案不支持PostgreSQL,因此使用Sequelize的“”“”“ best”“”“替代方案将使用该ON CONFLICT语句进行手动查询。示例(打字稿):

const values: Array<Array<number | string>> = [
    [1, 'Apple', 'Red', 'Yummy'],
    [2, 'Kiwi', 'Green', 'Yuck'],
]

const query = 'INSERT INTO fruits (id, name, color, flavor) VALUES ' +
     values.map(_ => { return '(?)' }).join(',') +
     ' ON CONFLICT (id) DO UPDATE SET flavor = excluded.flavor;'

sequelize.query({ query, values }, { type: sequelize.QueryTypes.INSERT })
Run Code Online (Sandbox Code Playgroud)

这将建立一个查询,例如:

INSERT INTO 
    fruits (id, name, color, flavor)
VALUES 
    (1, 'Apple', 'Red', 'Yummy'),
    (2, 'Kiwi', 'Green', 'Yuck')
ON CONFLICT (id) DO UPDATE SET 
    flavor = excluded.flavor;
Run Code Online (Sandbox Code Playgroud)

可以说,这不是必须手动构建查询的理想解决方案,因为它违反了使用sequelize的目的,但是,如果您不需要的是一次性查询,则可以使用此方法。


Pir*_*App 7

2019年更新

适用于所有方言,前提是匹配某个最低版本

是对相同内容的源代码的引用

  • 请注意,各个选项可能适用于所有方言,也可能不适用于例如 updateOnDuplicate 仅适用于 MySQL、MariaDB、SQLite 和 Postgres

  • ignoreDuplicates 选项不适用于 MSSQL

另请检查源代码中的此代码块

if (Array.isArray(options.updateOnDuplicate) && options.updateOnDuplicate.length) {
    options.updateOnDuplicate = _.intersection(
        _.without(Object.keys(model.tableAttributes), createdAtAttr),
        options.updateOnDuplicate
    );
} else {
    return Promise.reject(new Error('updateOnDuplicate option only supports non-empty array.'));
}
Run Code Online (Sandbox Code Playgroud)

updateOnDuplicate 必须是一个数组,不能为 true 或 false

因此,根据上述几点,您的代码应该是这样的

Employee.bulkCreate(data, {
    updateOnDuplicate: ['employeeName', 'employeeAge'],
});
Run Code Online (Sandbox Code Playgroud)

更新:

既然有人提到它不起作用,请尝试这个

models.Employee.bulkCreate(items, {
    returning: ['employeeId'],
    ignoreDuplicates: true
  })
Run Code Online (Sandbox Code Playgroud)


Yed*_*hin 5

2020年10月1日更新
续集版本:^6.3.5

该问题仍然存在。我们仍然不能bulkUpsert使用唯一的复合索引。bulkCreatewithupdateOnDuplicates尚不支持唯一复合索引。还有 PR 仍在等待合并,这可能会解决此问题:-
https://github.com/sequelize/sequelize/pull/12516
https://github.com/sequelize/sequelize/pull/12547

解决方法

目前,如果有人想要快速解决方法,则可以通过修改您自己的表属性、名称和数据来使用以下基于原始查询的包装器:-

const bulkUpsertIntoTable = async ({ bulkUpsertableData }) => {
  try {
    /* eslint-disable */
   // id column will automatically be incremented if you have set it to auto-increment
   const query = `INSERT INTO "Table" ("non_id_attr1", "non_id_attr2", "non_id_attr3","createdAt", "updatedAt") VALUES ${bulkUpsertableData
    .map((_) => "(?)")
    .join(
      ","
    )} ON CONFLICT ("non_id_attr1","non_id_attr2") DO UPDATE SET "non_id_attr1"=excluded."non_id_attr1", "non_id_attr2"=excluded."non_id_attr2", "non_id_attr3"=excluded."non_id_attr3",  "updatedAt"=excluded."updatedAt" RETURNING "id","non_id_attr1","non_id_attr2","non_id_attr3","createdAt","updatedAt";`;
    /* eslint-enable */

    return await models.sequelize.query(query, {
      replacements: bulkUpsertableData,//------> dont forget to pass your data here
      type: models.Sequelize.QueryTypes.INSERT,
      // transaction:t -----> if required to be done in transaction
    });
  } catch (error) {
    console.error("Bulk Upserting into Table:", error);
    throw error;
  }
};
Run Code Online (Sandbox Code Playgroud)

重要的一点是bulkUpsertableData在它应该在的地方创建Array<Array> ie:- [[]]。创建示例:-

// with reference to above wrapper function
const bulkUpsertableData = Object.keys(myObjectData).map(type => [
      myObjectData[type],// -----> non_id_attr1
      type, // -----> non_id_attr2
      someOtherRandomValue, // -----> non_id_attr3
      new Date(), // -----> created_at
      new Date(), // -----> updated_at
]);

// response will have all the raw attributes mentioned in RETURNING clause
const upsertedTableResponse = await bulkUpsertIntoTable({ bulkUpsertableData });
Run Code Online (Sandbox Code Playgroud)