如何重复 SQL 插入直到 pg-promise 成功?
How to repeat SQL insertion until successful with pg-promise?
在我的程序中,我将一些数据插入 table 并取回它的 ID,我需要确保将该 ID 输入到另一个 table 中,并使用随机生成的唯一字符串。但是,如果尝试插入一个已经存在的随机字符串而导致插入失败,我该如何重复插入直到成功?
我正在使用 pg-promise
与 postgreSQL 对话。我可以 运行 编写这样的程序,在随机字符串不存在的情况下将数据插入到两个 table 中:
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 的简单解决方案可供我使用。
最简单的方法是将它放入一个方法中,然后在 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;
});
}
我鼓励问题的作者寻求一个纯粹的SQL解决方案来解决他的问题,因为就性能而言,它比其他任何方法都更有效率。
但由于问题是关于如何使用 pg-promise 重新 运行 查询,除了已经发布的示例之外,我将提供一个示例,除了不获取和释放每个连接尝试,加上适当的数据完整性。
db.tx(t => {
// BEGIN;
return t.one('INSERT INTO table_one(text) VALUES() RETURNING id', 'abcd123', a => +a.id)
.then(id => {
var f = attempts => t.none('INSERT INTO table_two(id, randstr) VALUES(, 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;
});
因为你正在尝试重新运行一个依赖查询,你不应该让第一个查询保持成功,如果你对第二个查询的所有尝试都失败了,你应该回滚所有更改,即使用事务 - 方法 tx,如代码所示。
这就是我们在交易中拆分您的 WITH
查询以确保完整性的原因。
更新
下面是它的更好版本。因为事务内部的错误需要隔离,为了避免破坏事务堆栈,每次尝试都应该在自己的内部SAVEPOINT
,这意味着使用另一个事务级别:
db.tx(t => {
// BEGIN;
return t.one('INSERT INTO table_one(name) VALUES() 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(, 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;
});
我还建议使用 pg-monitor,这样您就可以看到并理解下面发生的事情,以及实际上正在执行的查询。
P.S。我是 pg-promise.
的作者
在我的程序中,我将一些数据插入 table 并取回它的 ID,我需要确保将该 ID 输入到另一个 table 中,并使用随机生成的唯一字符串。但是,如果尝试插入一个已经存在的随机字符串而导致插入失败,我该如何重复插入直到成功?
我正在使用 pg-promise
与 postgreSQL 对话。我可以 运行 编写这样的程序,在随机字符串不存在的情况下将数据插入到两个 table 中:
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 的简单解决方案可供我使用。
最简单的方法是将它放入一个方法中,然后在 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;
});
}
我鼓励问题的作者寻求一个纯粹的SQL解决方案来解决他的问题,因为就性能而言,它比其他任何方法都更有效率。
但由于问题是关于如何使用 pg-promise 重新 运行 查询,除了已经发布的示例之外,我将提供一个示例,除了不获取和释放每个连接尝试,加上适当的数据完整性。
db.tx(t => {
// BEGIN;
return t.one('INSERT INTO table_one(text) VALUES() RETURNING id', 'abcd123', a => +a.id)
.then(id => {
var f = attempts => t.none('INSERT INTO table_two(id, randstr) VALUES(, 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;
});
因为你正在尝试重新运行一个依赖查询,你不应该让第一个查询保持成功,如果你对第二个查询的所有尝试都失败了,你应该回滚所有更改,即使用事务 - 方法 tx,如代码所示。
这就是我们在交易中拆分您的 WITH
查询以确保完整性的原因。
更新
下面是它的更好版本。因为事务内部的错误需要隔离,为了避免破坏事务堆栈,每次尝试都应该在自己的内部SAVEPOINT
,这意味着使用另一个事务级别:
db.tx(t => {
// BEGIN;
return t.one('INSERT INTO table_one(name) VALUES() 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(, 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;
});
我还建议使用 pg-monitor,这样您就可以看到并理解下面发生的事情,以及实际上正在执行的查询。
P.S。我是 pg-promise.
的作者