У меня есть шаблон, которому я почти всегда следую, где, если мне нужно завершить операцию в транзакции, я делаю это:
BEGIN TRANSACTION
SAVE TRANSACTION TX
-- Stuff
IF @error <> 0
ROLLBACK TRANSACTION TX
COMMIT TRANSACTION
Это послужило мне достаточно хорошо в прошлом, но после многих лет использования этого паттерна (и копирования кода выше) я внезапно обнаружил недостаток, который стал полным шоком.
Довольно часто у меня будет хранимая процедура, вызывающая другие хранимые процедуры, которые используют один и тот же шаблон. Я обнаружил (к моей стоимости), что, поскольку я везде использую одно и то же имя точки сохранения, я могу попасть в ситуацию, когда моя внешняя транзакция частично зафиксирована - в точности противоположность атомарности, которую я пытаюсь достичь .
Я собрал пример, который показывает проблему. Это один пакет (без вложенных хранимых процедур), и это выглядит немного странно, так как вы, вероятно, не использовали бы одно и то же имя точки сохранения дважды в одном пакете, но мой реальный сценарий будет слишком запутанным для публикации.
CREATE TABLE Test (test INTEGER NOT NULL)
BEGIN TRAN
SAVE TRAN TX
BEGIN TRAN
SAVE TRAN TX
INSERT INTO Test(test) VALUES (1)
COMMIT TRAN TX
BEGIN TRAN
SAVE TRAN TX
INSERT INTO Test(test) VALUES (2)
COMMIT TRAN TX
DELETE FROM Test
ROLLBACK TRAN TX
COMMIT TRAN TX
SELECT * FROM Test
DROP TABLE Test
Когда я выполняю это, он перечисляет одну запись со значением "1". Другими словами, , хотя я откатил свою внешнюю транзакцию , запись была добавлена в таблицу.
Что происходит, так это то, что ROLLBACK TRANSACTION TX
на внешнем уровне откатывается до последнего SAVE TRANSACTION TX
на внутреннем уровне. Теперь, когда я все это записываю, я вижу логику: сервер просматривает файл журнала, рассматривая его как линейный поток транзакций; он не понимает вложенность / иерархию, подразумеваемую вложенностью транзакций (или, в моем реальном сценарии, вызовами других хранимых процедур).
Итак, ясно, что мне нужно начать использовать уникальные имена точек сохранения, а не вслепую везде использовать «TX». Но - и вот где я наконец дошел до сути - есть ли способ сделать это с возможностью копирования, чтобы я мог все еще использовать один и тот же код везде? Можно ли как-нибудь автоматически сгенерировать имя точки сохранения на лету? Есть ли соглашение или лучшие практики для подобных вещей?
Не сложно придумать уникальное имя каждый раз, когда вы начинаете транзакцию (может основываться на имени SP или что-то в этом роде), но я действительно волнуюсь, что в конечном итоге возникнет конфликт - и вы не станете знать об этом, потому что вместо того, чтобы вызвать ошибку, он просто молча уничтожает ваши данные ...: - (