Как я могу гарантировать, что я сбрасываю права входа в систему в MULTI_USER при сбое сценария SQL? - PullRequest
3 голосов
/ 03 февраля 2012

У меня есть следующий сценарий SQL:

USE MyDatabase

ALTER DATABASE MyDatabase SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO

BEGIN TRANSACTION

-- Write to tables here

COMMIT TRANSACTION

ALTER DATABASE MyDatabase SET MULTI_USER
GO

Я хочу убедиться, что моя база данных не доступна во время выполнения сценария, поэтому я устанавливаю базу данных на SINGLE_USER вtop.

Теперь, если что-то внутри транзакции завершится неудачно (например, синтаксическая ошибка), я никогда не верну базу данных обратно к MULTI_USER, что означает, что она будет постоянно зависать в однопользовательском режиме (не то, что я хочу).

Есть ли способ гарантировать, что мы всегда возвращаемся к MULTI_USER, даже при сбое?

Я думаю, я ищу эквивалент блока finally в SQL, ноиз того, что я прочитал, этого не существует.Есть ли другой способ?

Стоит отметить, что я не могу переместить команды ALTER DATABASE в транзакцию, поскольку это недопустимо (так как они фиксируются немедленно).

1 Ответ

3 голосов
/ 03 февраля 2012

Вы должны использовать TRY CATCH , чтобы всегда возвращаться к MULTI_USER.

Вам просто нужно переместить команду, чтобы переключиться обратно на MULTI_USER сразу после блока TRY CATCH, поскольку FINALLY не поддерживается в SQL Server.

Я провел быстрый тест со следующим SQL, и он работал, как и ожидалось, в SQL Server 2005. Просто убедитесь, что вы ROLLBACK транзакции в блоке CATCH. Я использовал SELECT 1/0 для принудительного ввода кода в блок CATCH. В целях отладки я добавил операторы SELECT user_access_desc ..., чтобы показать, что база данных действительно переключается из однопользовательского режима в многопользовательский.

USE MyDatabase

ALTER DATABASE MyDatabase SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO

SELECT user_access_desc from sys.databases WHERE Name = 'MyDatabase'

DECLARE @errNum AS INT
DECLARE @errMsg AS VARCHAR(MAX)
SET @errNum = 0

BEGIN TRY

    BEGIN TRANSACTION
    SELECT 1/0
    COMMIT TRANSACTION

END TRY
BEGIN CATCH

    SELECT @errNum = ERROR_NUMBER(), @errMsg = ERROR_MESSAGE()
    ROLLBACK TRANSACTION

END CATCH

IF @errNum <> 0
    SELECT 'An error occurred: ' + CAST(@errNum AS VARCHAR) + '- ' + @errMsg

ALTER DATABASE MyDatabase SET MULTI_USER
GO

SELECT user_access_desc from sys.databases WHERE Name = 'MyDatabase'

РЕДАКТИРОВАТЬ
В моем исходном ответе у меня был оператор ALTER ... MULTI_USER внутри блока TRY и CATCH, но в этом не было необходимости, поэтому я переместил оператор сразу после блока TRY CATCH. Я также добавил некоторые сообщения об ошибках. Некоторые вещи, на которые следует обратить внимание при таком подходе:

  1. Если вы обрабатываете ошибки или создаете отчеты, вам нужно убедиться, что SQL не дает ошибок. Я, вероятно, записал бы значения @errNum и @errMsg в таблицу (обернутую в TRY CATCH), переключился бы обратно в режим MULTI_USER, а затем выполнил бы любые другие необходимые меры по обработке ошибок, так как локальные переменные пойдут выходит за рамки после оператора GO.
  2. На некоторые ошибки не влияет TRY CATCH. В документации, на которую я ссылался выше, перечисляются эти условия.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...