/ / Як повторити вставку SQL до успіху з pg-promise? - sql, node.js, postgresql, обіцянка, пг-обіцянка

Як повторити вставку SQL до успіху з pg-обіцянкою? - sql, node.js, postgresql, обіцянка, pg-обіцянка

У моїй програмі я вставляю деякі дані в таблицю іповерніть його "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-обещання, яке я можу використовувати.

Відповіді:

2 для відповіді № 1

Я б заохочував автора питання шукати чисто-SQL-рішення для своєї проблеми, оскільки з точки зору продуктивності він був би набагато ефективнішим, ніж будь-що інше.

Але оскільки питання стосувалося повторного виконання запитів pg-обіцянка, Я наведу приклад, крім вже опублікованого, за винятком того, щоб придбати та звільнити з'єднання для кожної спроби, плюс належну цілісність даних.

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

Оскільки ви намагаєтеся повторно запустити залежний запит,не дозволяйте першому запиту залишатися успішним, якщо всі ваші спроби з другим запитом завершуються невдало, вам слід повернути всі зміни назад, тобто використовувати метод транзакції - tx, як показано в коді.

Ось чому ми розділяємо ваш WITH запит всередині транзакції, щоб забезпечити таку цілісність.

UPDATE

Нижче наведено кращу версію. Оскільки помилки всередині транзакції повинні бути ізольовані, щоб уникнути порушення стека транзакцій, кожна спроба повинна бути в її власній 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;
});

Я б також запропонував використовувати pg-монітор, так що ви можете побачити і зрозуміти, що відбувається під ним, і які запити фактично виконуються.


P.S. Я "автор" pg-обіцянка.


1 для відповіді № 2

Найпростіший спосіб полягає в тому, щоб ввести його в метод, після чого повторно зателефонувати в улов:

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

}