在KnexJS中进行Upsert

时间:2016-07-29 12:41:40

标签: sql postgresql knex.js

我在PostgreSQL中有一个upsert查询,如:

INSERT INTO table
  (id, name)
values
  (1, 'Gabbar')
ON CONFLICT (id) DO UPDATE SET
  name = 'Gabbar'
WHERE
  table.id = 1

我需要使用knex来进行此upsert查询。如何解决这个问题?

5 个答案:

答案 0 :(得分:5)

所以我使用Dotnil's answer on Knex Issues Page中的以下建议解决了这个问题:

var data = {id: 1, name: 'Gabbar'};
var insert = knex('table').insert(data);
var dataClone = _.cloneDeep(data);

delete dataClone.id;

var update = knex('table').update(dataClone).whereRaw('table.id = ' + data.id);
var query = util.format('%s ON CONFLICT (id) DO UPDATE SET %s',
  insert.toString(),
  update.toString().replace(/^update\s.*\sset\s/i, '')
);

return knex.raw(query)
.then(function(dbRes){
  // stuff
});

希望这有助于某人。

答案 1 :(得分:4)

knex@v0.21.10+ 开始,引入了一个新方法 onConflict

Official documentation 说:

<块引用>

为 PostgreSQL、MySQL 和 SQLite 数据库实施。一种 用于指定替代行为的插入查询的修饰符 冲突的情况。当表具有 PRIMARY 时发生冲突 KEY 或列上的 UNIQUE 索引(或一组上的复合索引 列)并且被插入的行的值与被插入的行的值相同 已存在于表中的那些列中。默认行为 如果发生冲突,则引发错误并中止查询。使用 此方法您可以将此行为更改为静默忽略 通过使用 .onConflict().ignore() 或更新现有的错误 使用新数据行(执行“UPSERT”) .onConflict().merge().

所以在你的情况下,实现将是:

knex('table')
  .insert({
    id: id,
    name: name
  })
  .onConflict('id')
  .merge()

答案 2 :(得分:1)

还有我能想到的另一种方法!

exports.upsert = (t, tableName, columnsToRetain, conflictOn) => {
    const insert = knex(tableName)
        .insert(t)
        .toString();
    const update = knex(tableName)
        .update(t)
        .toString();
    const keepValues = columnsToRetain.map((c) => `"${c}"=${tableName}."${c}"`).join(',');
    const conflictColumns = conflictOn.map((c) => `"${c.toString()}"`).join(',');
    let insertOrUpdateQuery = `${insert} ON CONFLICT( ${conflictColumns}) DO ${update}`;
    insertOrUpdateQuery = keepValues ? `${insertOrUpdateQuery}, ${keepValues}` : insertOrUpdateQuery;
    insertOrUpdateQuery = insertOrUpdateQuery.replace(`update "${tableName}"`, 'update');
    insertOrUpdateQuery = insertOrUpdateQuery.replace(`"${tableName}"`, tableName);
    return Promise.resolve(knex.raw(insertOrUpdateQuery));
};

答案 3 :(得分:0)

我已经为此创建了一个函数described it on the knex github issues page(以及处理复合唯一索引的一些问题)。

答案 4 :(得分:0)

非常简单。

添加到 Dorad 的答案中,您可以使用合并关键字选择要更新插入的特定列。

knex('table')
  .insert({
    id: id,
    name: name
  })
  .onConflict('id')
  .merge(['name']); // put column names inside an array which you want to merge.