在KnexJS中进行Upsert

myu*_*suf 14 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
Run Code Online (Sandbox Code Playgroud)

我需要使用knex来进行此upsert查询.怎么去这个?

myu*_*suf 8

因此,我使用Dotnil在Knex问题页面上的回答中的以下建议解决了这一问题

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
});
Run Code Online (Sandbox Code Playgroud)

希望这对某人有帮助。


Tim*_*Tim 7

我为此创建了一个函数,并在 knex github 问题页面上对其进行了描述(以及处理复合唯一索引的一些问题)。

const upsert = (params) => {
  const {table, object, constraint} = params;
  const insert = knex(table).insert(object);
  const update = knex.queryBuilder().update(object);
  return knex.raw(`? ON CONFLICT ${constraint} DO ? returning *`, [insert, update]).get('rows').get(0);
};
Run Code Online (Sandbox Code Playgroud)

用法示例:

const objToUpsert = {a:1, b:2, c:3}

upsert({
    table: 'test',
    object: objToUpsert,
    constraint: '(a, b)',
})
Run Code Online (Sandbox Code Playgroud)

关于复合可空索引的说明

如果你有一个复合索引 (a,b) 并且 b 可以为空,那么值 (1, NULL) 和 (1, NULL) 被 Postgres 认为是相互唯一的(我也不明白)。

  • 虽然您的链接确实有帮助,但如果您也在这里分享答案会更好。谢谢。 (5认同)

Dor*_*rad 6

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

官方文档说:

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

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

knex('table')
  .insert({
    id: id,
    name: name
  })
  .onConflict('id')
  .merge()
Run Code Online (Sandbox Code Playgroud)