TSQL Try / Catch внутри транзакции или наоборот?
Я пишу сценарий, который удалит записи из нескольких таблиц, но перед удалением он должен вернуть счетчик для подтверждения пользователем перед фиксацией.
Это краткое изложение сценария.
BEGIN TRANSACTION SCHEDULEDELETE
BEGIN TRY
DELETE -- delete commands full SQL cut out
DELETE -- delete commands full SQL cut out
DELETE -- delete commands full SQL cut out
PRINT 'X rows deleted. Please commit or rollback.' --calculation cut out.
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage
ROLLBACK TRANSACTION SCHEDULEDELETE
PRINT 'Error detected, all changes reversed.'
END CATCH
--COMMIT TRANSACTION SCHEDULEDELETE --Run this if count correct.
--ROLLBACK TRANSACTION SCHEDULEDELETE --Run this if there is any doubt whatsoever.
Это мой первый раз, когда я пишу транзакцию, правильно/лучше всего иметь блок TRY / CATCH внутри транзакции или транзакция должна быть внутри блока TRY?
важным фактором в этом сценарии является то, что пользователь должен вручную зафиксируйте транзакцию.
3 ответов
только открыть транзакцию, как только вы находитесь внутри TRY
блок и непосредственно перед фактическим утверждением, и зафиксируйте его сразу. Не ждите, пока ваш элемент управления перейдет в конец пакета для фиксации транзакций.
если что-то пойдет не так, пока вы находитесь в TRY
блок и вы открыли сделку, то контроль перейдет к CATCH
заблокировать. Просто откатите транзакцию и выполните другую обработку ошибок по мере необходимости.
я добавлена небольшая проверка для любой открытой транзакции с помощью @@TRANCOUNT
функция перед фактическим откатом транзакции. В этом сценарии нет особого смысла. Это более полезно, когда вы делаете некоторые проверки проверки в TRY
блок перед открытием транзакции, такой как проверка значений param и других вещей и повышение ошибки в TRY
блок, если какая-либо из проверок не удастся. В этом случае элемент управления перейдет на CATCH
блок, даже не открывая транзакцию. Там вы можете проверить любую открытую транзакцию и откат, если они есть. В вашем случае вам действительно не нужно проверять наличие открытой транзакции, так как вы не введете CATCH
блок, если что-то пойдет не так в вашей сделке.
не задать после выполнения DELETE
операция, должна ли она быть зафиксирована или откат; выполните все эти проверки перед открытием транзакции. Как только транзакция будет открыта, зафиксируйте ее сразу и в случае из любых ошибок выполните обработку ошибок (вы делаете хорошую работу, получая подробную информацию, используя почти все функции ошибок).
BEGIN TRY
BEGIN TRANSACTION SCHEDULEDELETE
DELETE -- delete commands full SQL cut out
DELETE -- delete commands full SQL cut out
DELETE -- delete commands full SQL cut out
COMMIT TRANSACTION SCHEDULEDELETE
PRINT 'X rows deleted. Operation Successful Tara.' --calculation cut out.
END TRY
BEGIN CATCH
IF (@@TRANCOUNT > 0)
BEGIN
ROLLBACK TRANSACTION SCHEDULEDELETE
PRINT 'Error detected, all changes reversed'
END
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage
END CATCH
никогда не ждите, пока конечный пользователь зафиксирует транзакцию, если это не база данных в однопользовательском режиме.
короче говоря, речь идет о блокировке. Ваша транзакция будет принимать некоторые эксклюзивные блокировки на обновляемых ресурсах и будет держаться за эти блокировки до тех пор, пока транзакция не будет завершена (зафиксирована или откат). Никто не сможет прикоснуться к этим рядам. Существуют различные проблемы, если изоляция моментальных снимков используется с очисткой хранилища версий.
лучше сначала выпустить a выберите запрос, чтобы определить количество строк, предоставьте это конечному пользователю и после подтверждения выполните фактическое удаление.
в дополнение к хорошим советам М. Али и Дина выше, немного помогите тем, кто хочет использовать новую (er) парадигму try CATCH THROW в SQL SERVER:
(Я не мог легко найти полный синтаксис, поэтому добавлять его здесь)
суть : здесь
пример кода хранимой процедуры здесь (из моего gist):
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROC [dbo].[pr_ins_test]
@CompanyID INT
AS
SET NOCOUNT ON
BEGIN
DECLARE @PreviousConfigID INT
BEGIN TRY
BEGIN TRANSACTION MYTRAN; -- Give the transaction a name
SELECT 1/0 -- Generates divide by zero error causing control to jump into catch
PRINT '>> COMMITING'
COMMIT TRANSACTION MYTRAN;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
BEGIN
PRINT '>> ROLLING BACK'
ROLLBACK TRANSACTION MYTRAN; -- The semi-colon is required (at least in SQL 2012)
END
THROW
END CATCH
END