在我的程序中,我将一些数据插入到表中并返回它的id,我需要确保将该id输入到具有唯一随机生成的字符串的另一个表中。但是,如果插入尝试插入已存在的随机字符串失败,我怎么能重复插入直到它成功?
我正在使用pg-promise
与postgreSQL交谈。我可以运行这样的程序,在没有随机字符串的情况下将数据插入到两个表中:
db.none(
`
WITH insert_post AS
(
INSERT INTO table_one(text) VALUES('abcd123')
RETURNING id
)
INSERT INTO table_two(id, randstr)
VALUES((SELECT id FROM insert_post), '${randStrFn()}')
`
)
.then(() => console.log("Success"))
.catch(err => console.log(err));
我不确定是否有可以使用的基于SQL / JS / pg-promise的简单解决方案。
答案 0 :(得分:3)
我鼓励问题的作者为他的问题寻求一种纯粹的SQL解决方案,因为在性能方面它会比其他任何方法都更有效率。
但由于问题是关于如何使用pg-promise重新运行查询,我将提供一个示例,除了已经发布的示例之外,除了不为每次尝试获取和释放连接,以及正确的数据完整性
db.tx(t => {
// BEGIN;
return t.one('INSERT INTO table_one(text) VALUES($1) RETURNING id', 'abcd123', a => +a.id)
.then(id => {
var f = attempts => t.none('INSERT INTO table_two(id, randstr) VALUES($1, randStrFn())', id)
.catch(error => {
if (--attempts) {
return f(attempts); // try again
}
throw error; // give up
});
return f(3); // try up to 3 times
});
})
.then(data => {
// COMMIT;
// success, data = null
})
.catch(error => {
// ROLLBACK;
});
由于您尝试重新运行从属查询,因此不应让第一个查询保持成功,如果您对第二个查询的所有尝试都失败,则应该回滚所有更改,即使用事务 - 方法{ {3}},如代码所示。
这就是我们在事务中拆分WITH
查询的原因,以确保这种完整性。
<强>更新强>
下面是它的更好版本。因为事务中的错误需要被隔离,为了避免破坏事务堆栈,每次尝试都应该在自己的SAVEPOINT
内,这意味着使用另一个事务级别:
db.tx(t => {
// BEGIN;
return t.one('INSERT INTO table_one(name) VALUES($1) RETURNING id', 'abcd123', a => +a.id)
.then(id => {
var f = attempts => t.tx(sp => {
// SAVEPOINT level_1;
return sp.none('INSERT INTO table_two(id, randstr) VALUES($1, randStrFn())', id);
})
.catch(error => {
// ROLLBACK TO SAVEPOINT level_1;
if (--attempts) {
return f(attempts); // try again
}
throw error; // give up
});
return f(3); // try up to 3 times
});
})
.then(data => {
// 1) RELEASE SAVEPOINT level_1;
// 2) COMMIT;
})
.catch(error => {
// ROLLBACK;
});
我还建议使用tx,这样您就可以看到并了解底层发生的事情,以及实际执行的查询。
P.S。我是pg-monitor的作者。
答案 1 :(得分:1)
最简单的方法是将其放入方法中,然后在catch中重新调用它:
const insertPost = (post, numRetries) => {
return
db.none(
`
WITH insert_post AS
(
INSERT INTO table_one(text) VALUES('abcd123')
RETURNING id
)
INSERT INTO table_two(id, randstr)
VALUES((SELECT id FROM insert_post), '${randStrFn()}')
`
)
.then(() => console.log("Success"))
.catch(err => {
console.log(err)
if (numRetries < 3) {
return self.insertPost(post, numRetries + 1);
}
throw err;
});
}