У меня есть база данных, которую я «выпускаю» еженедельно, то есть я публикую резервную копию, с которой люди могут начать, и я публикую сценарий обновления, который они могут использовать для обновления с прошлой недели (чтобы они могли максимально сохранить свои текущие данные). Этот скрипт, конечно, содержит много DDL — CREATE TABLE
, ALTER TABLE
и так далее. Базовая структура его такова:
/*
HOW TO USE THIS SCRIPT
1. Run it against your existing DB
2. Check whether there were any errors
3. If there were, issue a rollback by highlighting this:
ROLLBACK
and executing it
4. If there weren't, issue a commit by highlighting this:
COMMIT
and executing it
5. !!! Not doing either of these will leave a transaction open, which will
probably cause all further queries to time out till you do !!!
*/
SET XACT_ABORT ON;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
GO
-- Boilerplate checking and whatnot goes here
GO
-- Guts of operation, part 1
GO
-- Guts of operation, part 2
GO
-- Guts of operation, part 3
GO
-- . . .
GO
-- Guts of operation, part N
GO
-- Boilerplate cleanup stuff here
GO
Вы заметите, что я оставил транзакцию открытой и сказал им завершить ее вручную, в зависимости от того, что произойдет. Я бы предпочел, чтобы это было автоматически, но я отчаялся в этом, учитывая, что все это всегда представляет собой длинную серию из нескольких пакетов, а блоки TRY
, конечно, не могут охватывать пакеты. Таким образом, недавнее добавление SET XACT_ABORT ON;
немного облегчило боль. Несмотря на это, я пробовал это сам, так как без него, и это не имеет значения для сценария. Тем не мение.
Недавно в одном из этих сценариев были операторы для создания таблиц и другие операторы для добавления проверочных ограничений к существующим таблицам. Один из моих пользователей запустил скрипт и столкнулся с ошибкой ограничения; оказывается, у него были ранее существовавшие данные, нарушающие ограничение. Хорошо, нет проблем, сделайте ROLLBACK
. Верно, не нужно было этого делать, XACT_ABORT
уже сделал это. Идите и исправьте строки данных… готово. Теперь попробовать еще раз! Э-э... что? На этот раз ошибки в ограничении нет, но в операторах CREATE TABLE
возникают ошибки, говорящие, что таблицы уже существуют…! Хм? Не откатился?
Мы закончили восстановление из резервной копии, повторное исправление данных и повторный запуск. Но это ни здесь, ни там.
Итак, уважаемые читатели: Как создание этих таблиц пережило откат транзакции? Как я могу заставить их этого не делать?
РЕДАКТИРОВАТЬ: Хорошо, вот пример, который вы можете запустить.
USE tempdb;
GO
CREATE DATABASE example;
GO
USE example;
GO
CREATE TABLE foo (a INT);
GO
INSERT INTO foo
VALUES (100);
GO
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
GO
ALTER TABLE foo ADD CHECK (a < 10);-- Gives error "The ALTER TABLE statement conflicted with the CHECK constraint…", as expected
GO
CREATE TABLE bar (b INT);
GO
ROLLBACK;-- Gives error "The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION." Huh? Where did our transaction go?
GO
SELECT *
FROM bar;-- Gives no error. Table still exists! NOT expected!
GO
USE tempdb;
GO
DROP DATABASE example;