Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Как да се справяме с грешки в вложените транзакции на SQL Server

В тази статия ще разгледаме вложените транзакции на SQL Server, транзакционен блок с една или няколко транзакции.

Изображението описва прост модел на вложената транзакция.

Вътрешната транзакция е съхранена процедура, която се състои от транзакционни блокове. MSDN препоръчва „да поддържате транзакциите възможно най-кратки“, което е напълно противоположно на първия подход. Според мен не препоръчвам използването на вложени транзакции. Все пак понякога се налага да ги използваме, за да решим някои бизнес проблеми.

Така ще разберем:

  • Какво ще се случи, когато външна транзакция бъде отменена или ангажиментирана?
  • Какво ще се случи, когато вътрешна транзакция бъде отменена или ангажиментирана?
  • Как да се справяме с грешките при вложени транзакции?

Като начало ще създадем демонстрационна таблица и ще тестваме възможни случаи.

USE AdventureWorks
-----Create Demo Table----
CREATE TABLE CodingSightDemo
(NumberValue VARCHAR(20))

Случай 1:И външните, и вътрешните транзакции са ангажирани.

TRUNCATE TABLE CodingSightDemo  
--<*************OUTHER TRANSACTION START*************>
BEGIN TRAN				   
INSERT INTO CodingSightDemo	
VALUES('One')				
--<INNER TRANSACTION START>
BEGIN TRAN 					
INSERT INTO CodingSightDemo 		
VALUES('Two') 			
COMMIT TRAN	 			
--< INNER TRANSACTION END>
INSERT INTO CodingSightDemo 								VALUES('Three')				  
COMMIT TRAN		
--<************* OUTHER TRANSACTION END*************>
SELECT * FROM CodingSightDemo

В този случай всички записи се вмъкват успешно в таблицата. Предполагахме, че всеки оператор INSERT не връща грешка.

Случай 2:Външната транзакция е отменена , вътрешната транзакция е ангажирана .

TRUNCATE TABLE CodingSightDemo  
--<*************OUTHER TRANSACTION START*************>
BEGIN TRAN				   
INSERT INTO CodingSightDemo	
VALUES('One')				
--<INNER TRANSACTION START>
BEGIN TRAN 					
INSERT INTO CodingSightDemo 		
VALUES('Two') 			
COMMIT TRAN	 			
--< INNER TRANSACTION END>
INSERT INTO CodingSightDemo VALUES('Three')				  
rollback TRAN		
--<************* OUTHER TRANSACTION END*************>
SELECT * FROM CodingSightDemo

Както можете да видите, записите не се вмъкват в таблицата, защото вътрешната транзакция е част от външната транзакция. Поради тази причина вътрешната транзакция се връща назад.

Случай 3:Външната транзакция е ангажирана , вътрешната транзакция е връщана назад .

TRUNCATE TABLE CodingSightDemo  
--<*************OUTHER TRANSACTION START*************>
BEGIN TRAN				   
INSERT INTO CodingSightDemo	
VALUES('One')				
--<INNER TRANSACTION START>
BEGIN TRAN 					
INSERT INTO CodingSightDemo 		
VALUES('Two') 			
ROLLBACK TRAN	 			
--< INNER TRANSACTION END>
INSERT INTO CodingSightDemo VALUES('Three')				  
COMMIT TRAN		
--<************* OUTHER TRANSACTION END*************>
SELECT * FROM CodingSightDemo

В този случай получихме грешка и вмъкнахме последното изявление в таблицата. В резултат на това възникват някои въпроси:

  • Защо получихме грешка?
  • Защо последният оператор INSERT беше добавен към таблицата?

По правило операторът ROLLBACK TRAN връща обратно всички отворени транзакции, изпълнени в текущата сесия. Не можем да напишем заявка, защото ще върне грешка.

BEGIN TRAN
INSERT INTO CodingSightDemo	
VALUES('One')	
BEGIN TRAN
INSERT INTO CodingSightDemo	
VALUES('Two')	
ROLLBACK TRAN
ROLLBACK TRAN

Ще разгледаме как това правило може да повлияе на нашия случай. Инструкцията ROLLBACK TRAN връща вътрешните и външните транзакции. Поради тази причина получаваме грешка при изпълнение на оператора COMMIT TRAN, тъй като няма отворени транзакции.

След това ще добавим изявление за обработка на грешки към тази заявка и ще го модифицираме въз основа на подхода за защитно програмиране (както Уикипедия заявява:Защитното програмиране е форма на защитен дизайн, предназначен да гарантира непрекъснатата функция на част от софтуера при непредвидени обстоятелства). Когато напишем заявка, без да се грижим за обработката на грешки и получим грешка, може да се сблъскаме с нарушаване на целостта на данните.

Със следващия скрипт ще използваме точки за запис. Те маркират точка в транзакцията и ако желаете, можете да върнете обратно всички DML (език за манипулиране на данни) изрази до маркираната точка.

BEGIN TRY
BEGIN TRAN				   
INSERT INTO CodingSightDemo	
VALUES('One')				
--<INNER TRANSACTION START>
SAVE TRANSACTION innerTRAN
BEGIN TRY
BEGIN TRAN 					
INSERT INTO CodingSightDemo 		
VALUES('Two') 			
COMMIT TRAN
END TRY		
BEGIN CATCH
IF XACT_STATE() <> 0
BEGIN 
ROLLBACK TRANSACTION innerTRAN
PRINT 'Roll back occurs for inner tran'
END
IF XACT_STATE() <> 0
BEGIN 
COMMIT TRAN 
PRINT 'Commit occurs for firt open tran'
END
END CATCH
--< INNER TRANSACTION END>
INSERT INTO CodingSightDemo VALUES('Three')				  
COMMIT TRAN		
END TRY
BEGIN CATCH
BEGIN
IF XACT_STATE() <> 0
ROLLBACK TRAN 
PRINT 'Roll back occurs for outer tran'
END
END CATCH
--<************* OUTHER TRANSACTION END*************>
SELECT * FROM CodingSightDemo

Тази заявка ще се справи с грешката, когато вътрешната транзакция получи грешка. Освен това външните транзакции се извършват успешно. Въпреки това, в някои случаи, ако вътрешната транзакция получи грешка, външната транзакция трябва да се върне назад. В този случай ще използваме локална променлива, която ще запази и предаде стойността на състоянието на грешка на вътрешната заявка. Ще проектираме външната заявка с тази стойност на променливата и заявката ще бъде както следва.

--<*************OUTHER TRANSACTION START*************>
DECLARE @innertranerror as int=0
BEGIN TRY
BEGIN TRAN				   
INSERT INTO CodingSightDemo	
VALUES('One')				
--<INNER TRANSACTION START>
SAVE TRANSACTION innerTRAN
BEGIN TRY
BEGIN TRAN 					
INSERT INTO CodingSightDemo 		
VALUES('Two') 			
COMMIT TRAN
END TRY		
BEGIN CATCH
IF XACT_STATE() <> 0
BEGIN 
SET @innertranerror=1
ROLLBACK TRANSACTION innerTRAN
PRINT 'Roll back occurs for inner tran'
END
IF XACT_STATE() <> 0
BEGIN 
COMMIT TRAN 
PRINT 'Commit occurs for firt open tran'
END
END CATCH
--< INNER TRANSACTION END>
INSERT INTO CodingSightDemo VALUES('Three')	
if @innertranerror=0
BEGIN
COMMIT TRAN	
END
IF @innertranerror=1
BEGIN
ROLLBACK TRAN
END

END TRY
BEGIN CATCH
BEGIN
IF XACT_STATE() <> 0
ROLLBACK TRAN 
PRINT 'Roll back occurs for outer tran'
END
END CATCH
--<************* OUTHER TRANSACTION END*************>
SELECT * FROM CodingSightDemo

Заключения

В тази статия проучихме вложени транзакции и анализирахме как да обработваме грешки в този тип заявка. Най-важното правило за този тип транзакция е да пишем защитни заявки, защото можем да получим грешка при външни или вътрешни транзакции. Поради тази причина трябва да проектираме поведението за обработка на грешки на заявката.

Препратки

Вложени транзакции

ЗАПАЗЕТЕ ТРАНЗАКЦИЯТА


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Какъв е добър начин да изрежете всички символи за интервал от низ в T-SQL без UDF и без CLR?

  2. SQL Server блокира достъпа до процедурата 'sys.sp_OACreate' на компонента 'Ole Automation Procedures'

  3. Разделяне на стойности, разделени със запетая, в колони на множество редове в Sql Server

  4. Възстановяване на паралелно архивиране на SQL Server -2

  5. Шаблон за проектиране за персонализирани полета в релационна база данни