我在PostgreSQL中有一个upsert查询,如:
INSERT INTO table
(id, name)
values
(1, 'Gabbar')
ON CONFLICT (id) DO UPDATE SET
name = 'Gabbar'
WHERE
table.id = 1
我需要使用knex来进行此upsert查询。如何解决这个问题?
答案 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
。
为 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.