Я разработчик C #, изучаю TSQL. Я написал такой сценарий:
begin transaction
--Insert into several tables
end transaction
Но мне сказали, что это не очень хорошая идея, и использовать что-то вроде этого:
BEGIN TRANSACTION;
BEGIN TRY
-- Generate a constraint violation error.
DELETE FROM Production.Product
WHERE ProductID = 980;
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;
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH;
IF @@TRANCOUNT > 0
COMMIT TRANSACTION;
GO
Не понимаю, почему второй пример более правильный. Будет ли первый работать иначе? Кажется, первый либо обновит все таблицы, либо вообще не обновит? Я не понимаю, почему необходима проверка @@TRANCOUNT
перед фиксацией.
3 ответа
Открывайте транзакцию только тогда, когда вы находитесь внутри блока try и непосредственно перед фактическим оператором. И сразу же фиксируйте его, не дожидаясь, пока ваш элемент управления перейдет в конец пакета, чтобы зафиксировать ваши транзакции.
После того, как вы находитесь в блоке Try и открыли транзакцию, если что-то пойдет не так, элемент управления перейдет к блоку CATCH. Просто откатите свою транзакцию там и выполните другую обработку ошибок по мере необходимости.
Я добавил небольшую проверку, прежде чем на самом деле откатить проверку транзакции для любой открытой транзакции с помощью функции @@ ROWCOUNT. В этом сценарии это не имеет большого значения. Это более полезно, когда вы выполняете некоторые проверки валидации в своем блоке try, прежде чем открывать транзакцию, например, проверять значения параметров и другие вещи и вызывать ошибку в блоке try, если какая-либо из проверок валидации не удалась.В этом случае управление перейдет к блоку catch даже не открывая там транзакцию, вы можете проверить наличие открытых транзакций и выполнить откат, если есть открытые. В вашем случае, как есть, вам действительно не нужно проверять открытую транзакцию, так как вы не войдете в блок catch, если внутри транзакции что-то не пойдет не так.
BEGIN TRY
BEGIN TRANSACTION
-- Multiple Inserts
INSERT INTO....
INSERT INTO....
INSERT INTO....
COMMIT TRANSACTION
PRINT 'Rows inserted successfully...'
END TRY
BEGIN CATCH
IF (@@TRANCOUNT > 0)
BEGIN
ROLLBACK TRANSACTION
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
Я в двух мыслях по поводу TRY ... CATCH in T-SQL.
Хотя это потенциально полезное дополнение к языку, тот факт, что он доступен, не всегда является причиной его использования.
Принимая ваш
DELETE FROM Table WHERE...
Пример (который, как я понимаю, является только примером). Единственный способ вывести из строя ошибку - это если код серьезно испортился из-за схемы. (например, если кто-то создает внешний ключ с таблицей на конце PK).
При надлежащем тестировании такое несоответствие между кодом и схемой никогда не должно попадать в производство. Предполагая, что это так, ИМХО «грубое», неопосредованное сообщение об ошибке, которое будет в результате, является лучшим индикатором того, что пошло не так, чем «вежливое» обертывание его в оператор SELECT для возврата к клиенту. (Что может быть равносильно упомянутому SeanLange антипаттерну try / squelch).
Для более сложных сценариев я вижу использование TRY ... CATCH. Хотя, IMHO, это не замена тщательной проверки входных параметров.
Я хочу изложить здесь свою точку зрения как разработчика C #:
В приведенном выше простом сценарии (просто вставка в несколько таблиц из сценария) нет причин для добавления попытки / улова, так как это не добавляет транзакции преимуществ. Оба примера приведут к одинаковому результату: либо будут вставлены все таблицы, либо их не будет. Состояние базы данных остается неизменным. (Поскольку COMMIT TRANSACTION никогда не вызывается, откат неявно вызывается сервером Sql в конце скрипта.)
Однако бывают случаи, когда в try / catch можно делать то, что невозможно с интегрированной обработкой ошибок. Например, запись ошибки в таблицу ошибок.
По моему опыту работы с C #, единственный раз, когда я могу использовать Try / Catch, - это когда есть вещи, которые находятся вне контроля разработчика, например, попытка открыть файл. В таком случае единственный способ управлять исключением, сгенерированным фреймворком .Net, - это использовать Try / Catch.
Если бы я выполнял хранимую процедуру и хотел бы вручную проверить состояние данных и вручную вызвать ROLLBACK TRANSACTION
, я бы это увидел. Но это все равно не потребует попытки / улова.
Похожие вопросы
Связанные вопросы
Новые вопросы
sql-server
Microsoft SQL Server — это система управления реляционными базами данных (RDBMS). Используйте этот тег для всех выпусков Microsoft SQL Server, включая Compact, Express, Azure, Fast-track, APS (ранее PDW) и Azure SQL DW. Не используйте этот тег для других типов СУБД (MySQL, PostgreSQL, Oracle и т. д.). Не используйте этот тег для вопросов по программному обеспечению и разработке мобильных устройств, если только он не связан напрямую с базой данных.