저장 프로 시저에 트랜잭션을 사용하지 마십시오


18

몇 가지 명령을 실행하는 저장 프로 시저가 있습니다. 이 명령이 저장 프로 시저 트랜잭션에서 래핑되는 것을 원하지 않습니다. 네 번째 명령이 실패하면 첫 번째, 두 번째 및 세 번째 명령이 그대로 유지되고 롤백되지 않기를 원합니다.

저장 프로 시저를 모두 하나의 큰 트랜잭션으로 실행하지 않는 방식으로 작성할 수 있습니까?

답변:


16

모든 트랜잭션이 단일 트랜잭션에서 실행되지는 않습니다. 이 예제를 살펴보십시오.

use TestDB;
go

if exists (select 1 from sys.tables where object_id = object_id('dbo.TestTranTable1'))
    drop table dbo.TestTranTable1;
create table dbo.TestTranTable1
(
    id int identity(1, 1) not null,
    some_int int not null
        default 1
);
go

insert into dbo.TestTranTable1
default values;
go 4

select *
from dbo.TestTranTable1;

if exists (select 1 from sys.sql_modules where object_id = object_id('dbo.ChangeValues'))
begin
    drop proc dbo.ChangeValues;
end
go
create proc dbo.ChangeValues
as
    update dbo.TestTranTable1
    set some_int = 11
    where id = 1;

    update dbo.TestTranTable1
    set some_int = 12
    where id = 2;

    update dbo.TestTranTable1
    set some_int = 13
    where id = 3;

    -- this will error out (arithmetic overflow)
    update dbo.TestTranTable1
    set some_int = 2147483648
    where id = 4;
go

exec dbo.ChangeValues;

select *
from dbo.TestTranTable1;

출력은 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

이벤트를 모니터하기 위해 확장 이벤트 세션을 작성하면 sql_transaction다음과 같은 결과가 출력됩니다 dbo.ChangeValues.

여기에 이미지 설명을 입력하십시오

위의 스크린 샷에서 볼 수 있듯이 네 가지 진술 각각에 대해 별도의 거래가 있습니다. 처음 3 개의 커밋과 마지막으로 커밋은 오류로 인해 롤백됩니다.


16

배치트랜잭션에 대해 약간의 혼동이있을 수 있다고 생각 합니다 .

트랜잭션 중 하나를 성공하거나 하나의 단위로 실패합니다 하나의 명령문이나 명령문 세트입니다. 모든 DDL 문은 트랜잭션 자체에 있습니다 (예 : 100 개의 행을 업데이트하지만 98 행에 오류가 발생하면 행이 업데이트되지 않습니다). 당신은 잘 사용으로 트랜잭션에서 일련의 문을 래핑 할 수 BEGIN TRANSACTION다음 중 하나 COMMIT또는 ROLLBACK.

배치 함께 실행되는 일련의 명령문입니다. 저장 프로시 저는 배치의 예입니다. 저장 프로 시저에서 한 명령문이 실패하고 오류 트래핑 (일반적으로 TRY/CATCH블록) 이 있으면 후속 명령문이 실행되지 않습니다.

저장된 proc 자체 또는 외부 범위 (이 절차를 호출하는 응용 프로그램 또는 저장된 proc와 같은)에 오류가 발생하여 오류가 발생하면 일괄 처리가 취소되는 문제가 의심됩니다. 이 경우 트래핑하는 모든 범위에서 오류를 처리하는 방법을 조정해야하므로 해결하기가 더 까다 롭습니다.


"저장 프로시 저는 배치의 예입니다"라는 기사를 찾지 못했습니다. Stored Procedure는 batch와 매우 유사하지만 Batch는 아닙니다. 주요 차이점은 다음과 같습니다. SP는 배치와 달리 미리 컴파일되어 여러 번 실행할 수 있습니다. 유사점은 다음과 같습니다.-한 번에 각 명령을 실행합니다. -하나의 명령이 실패하면 모든 이전 명령이 커밋됩니다 (트랜잭션에서 실행되지 않은 경우). 하나의 명령이 실패하면 다음 명령이 모두 실행되지 않습니다.
Ashi

6

SQL Server의 모든 것은 트랜잭션에 포함됩니다.

명시 적으로 지정하는 경우 begin transactionend transaction그것은이라고 명시 적 트랜잭션을 . 당신이하지 않으면, 그것은 암시 적 거래 입니다.

사용중인 모드를 전환하려면

set implicit_transactions on

또는

set implicit_transactions off

select @@OPTIONS & 2

위의 값이 2를 반환하면 암시 적 트랜잭션 모드입니다. 0을 반환하면 자동 커밋에있는 것입니다.

트랜잭션은 데이터베이스를 일관성있는 상태로 유지하기위한 전부 또는 아무것도 아닙니다.

CREATE TABLE [dbo].[Products](
    [ProductID] [int] NOT NULL,
    [ProductName] [varchar](25) NULL,
    [DatabaseName] [sysname] NOT NULL,
 CONSTRAINT [pk_Product_ID_ServerName] PRIMARY KEY CLUSTERED 
(
    [ProductID] ASC,
    [DatabaseName] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO


-- insert some data 
INSERT INTO [dbo].[Products]([ProductID], [ProductName], [DatabaseName])
SELECT 1, N'repl1_product1', N'repl1' UNION ALL
SELECT 1, N'repl2_product1_02', N'repl2' UNION ALL
SELECT 1, N'repl3_product1_03', N'repl3' UNION ALL
SELECT 2, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 2, N'repl2_product1', N'repl2' UNION ALL
SELECT 2, N'repl3_product1_03', N'repl3' UNION ALL
SELECT 3, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 3, N'repl2_product1_02', N'repl2' UNION ALL
SELECT 3, N'repl3_product1', N'repl3' UNION ALL
SELECT 4, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 4, N'repl2_product1_02', N'repl2' UNION ALL
SELECT 5, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 5, N'repl2_product1_02', N'repl2'

-지금 SP 생성-문자열 잘림으로 인해 처음 3 개는 성공하고 4 번째는 실패합니다 ...

IF OBJECT_ID ('usp_UpdateProducts', 'P') IS NOT NULL
    DROP PROCEDURE usp_UpdateProducts;
GO
create procedure usp_UpdateProducts
as 
begin try
update Products 
set ProductName = 'repl1_product1'
where DatabaseName = 'repl1'and ProductID = 1;
update Products
set ProductName = 'repl2_product1'
where DatabaseName = 'repl2' and ProductID = 2;
update Products
set ProductName = 'repl3_product1'
where DatabaseName = 'repl3' and ProductID = 3;
update Products
set ProductName = 'repl3_product1_03&&&&&&&&&&39399338492w9924389234923482' -- this will fail ...
where DatabaseName = 'repl3' and ProductID = 4;
SELECT 1/0;
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;
end catch
go

참조 : 항상 거래를 만드는 것은 나쁜 습관입니까?


3

저장 프로 시저가 기본적으로 작동하는 방식입니다. 저장 프로시 저는 트랜잭션 내에서 자동으로 래핑되지 않습니다.

첫 번째 오류가 발생했을 때 저장 프로 시저를 중지하려면 예를 들어 명령 2에 문제가있는 경우 일부 TRY / CATCH 로그인을 입력하여 반환해야합니다.


2

각 명령마다 개별 트랜잭션이 필요합니다. 저장된 트랜잭션으로이를 수행 할 수도 있습니다.

SAVE TRANSACTION (Transact-SQL)제품 설명서를 참조하십시오 .

모든 명령문이 암시 적 트랜잭션으로 랩핑되어 있기 때문에 개별 트랜잭션이 스토어드 프로 시저의 기본 동작임을 검증하려고합니다. 그러나 코드의 운명을 제어하기 위해 암시 적 트랜잭션에 의존해서는 안됩니다. 프로덕션 코드에서 트랜잭션이 처리되는 방식을 명시 적으로 제어하는 ​​것이 훨씬 좋습니다.


-2

BEGIN TRAN으로 각 부품을 분리하고 거래가 성공했는지 확인하십시오. 커밋 된 경우 롤백을 수행하십시오. 롤백은 모두 동일한 수준에서 실행되므로 실패한 경우 모두 롤백하지 않고도 각 섹션을 개별적으로 커밋 할 수 있습니다.

자세한 내용은 http://msdn.microsoft.com/en-us/library/ms188929.aspx를 참조하십시오.


1
스토어드 프로 시저 내에 하위 트랜잭션이 생성됩니까? 이상적으로는 가능하면 피하고 싶습니다
Matthew Steeples

1
트랜잭션 내에서 SP를 호출하면 위의 저장된 트랜잭션이 그 답입니다. sp가 호출되지 않으면 @mrdenny가 올바른 것입니다. SQL Server는 중첩 트랜잭션을 지원하지 않습니다.
StrayCatDBA

@StrayCatDBA는 명확하게 설명합니다. SQL Server에는 중첩 트랜잭션이 있지만 악의적입니다. SQL Server를 사용하면 중첩 트랜잭션이라고하는 다른 트랜잭션 내에서 트랜잭션을 시작할 수 있습니다. sqlskills.com/blogs/paul/… , msdn.microsoft.com/en-us/library/ms189336(v=sql.105).aspxsqlblog.com/blogs/kalen_delaney/archive/2007/08/13을
Kin Shah

2
명확하게 (그리고 링크를 클릭하지 않으려는 게으른 사람을 위해) 실제로 다른 거래를 시작하지는 않습니다. 일명 Paul의 게시물 제목 : "신화 : 중첩 된 트랜잭션은 실제입니다." 그들은 실제 거래가 아닙니다. 중첩 트랜잭션의 COMMIT는 @@ TRANCOUNT를 감소시키는 것 외에는 아무것도하지 않습니다. BEGIN TRAN / COMMIT을 중첩하면 오류가 발생하지 않지만 실제 중첩 변환과는 다릅니다.
StrayCatDBA
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.