如何在pg-promise成功之前重复SQL插入?

时间:2017-05-08 19:13:45

标签: sql node.js postgresql promise pg-promise

在我的程序中,我将一些数据插入到表中并返回它的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的简单解决方案。

2 个答案:

答案 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;
        });

}