2016-08-02 6 views
0

문제는 동시성 문제이지만, SQL Server 트랜잭션을 어떻게 처리해야하는지 잘 이해하지 못한다고 생각합니다. 외래 키 제약 조건 충돌 동시성 문제

나는 하나 개의 테이블에 새 행을 삽입하는 저장 프로 시저가 다음 RID에 외래 키 제약 조건이있는 다른 테이블의 레코드 만들기 위해 그 INSERT에 의해 생성 된 ID를 사용하여이 :

INSERT INTO tbl_Registrations ... 

DECLARE @RID int; 
--get newly-created registration ID 
SET @RID=(SELECT MAX(RID) FROM tbl_Registrations WHERE ...); 
INSERT INTO tbl_RegistrationCertificates VALUES (@RID, 9); 

을 이 저장 프로시 저는 웹 사이트의 사용자 상호 작용에서 호출되며 테스트 할 때마다 제대로 작동합니다. 그러나, 가끔 동안, 나는 다음과 같은 예외와 함께 우리의 오류 처리기에서 이메일을 얻을 것이다 :

System.Data.SqlClient.SqlException (0x80131904): The INSERT statement conflicted with the FOREIGN KEY constraint "FK_tbl_RegistrationCertificates_tbl_Registrations". 
The conflict occurred in ... table "dbo.tbl_Registrations", column 'RID'. 
The statement has been terminated. 

을하지만, 하나가 변화를 볼 때까지 내 사용자는 "업데이트"버튼을 클릭 영구 있습니다, 또는 메시지가 The statement has been terminated이라고 표시 되더라도 트랜잭션이 진행되고 있습니다. 전자 메일의 다른 정보를 사용하면 두 테이블이 실제로 업데이트 된 것을 볼 수 있기 때문입니다.

나는 여기에 정보의 톤을 포기하지 않을거야 알고,하지만 난에 대한 답을 찾을 수없는 큰 질문은 다음과 같습니다

  1. 는 문장의 끝에 세미콜론이 T에 아무것도 할 수 있나요 -SQL? 나는 INSERT 문장의 끝 부분에 세미콜론이 없다는 것을 알아 차렸다. (저는 평상시와 달리 버릇을 없애고 있지만, 다른 사람들이 만든 시스템에서 작업하고 있습니다.)
  2. SQL Server에 대해 더 많이 알고있는 사람들에게 이것은 동시성 문제처럼 보입니까?
  3. 동시성 문제처럼 보이는 경우 트랜잭션을 사용하여 완화 할 수 있습니까? 어떻게 처리합니까?

편집 : 나는 SP가 결국 오류를 던지고있는 것이 아니라는 것을 깨달았습니다. 웹 사이트에 RID가 있는지 확인하지 않고 삽입 작업을하는 다른 장소가 있습니다. 아직도 나는 대답을 알지 못했기 때문에 나는 질문에 기뻤다.

답변

1

T-SQL은 세미콜론을 명령 구분 기호로 처리하지만 항상 필요하지는 않습니다. 이 점에 대한 심층적 인 토론은 question에서 찾을 수 있습니다.

외래 키 오류에 관해서는, 당신이 제시 한 것처럼 저장 프로 시저가 몇 가지 이유로 몇 가지 개선점을 사용할 수 있습니다.

먼저 여기 거래를 사용해야 할 가능성이 큽니다. 등록 인증서 레코드가 추가되지 않는 한 등록 레코드를 추가하지 않으려면 전체 작업을 단일 트랜잭션으로 수행해야합니다.

둘째, ID 저장 열의 MAX 값을 선택하면 동시에 저장 프로 시저를 호출 할 때 동시성 문제의 원인이 될 수 있습니다. 스레드 A INSERT를 처리하지만 스레드 B INSERT 전에 SELECT MAX (RID)를 실행할 수없는 시나리오를 상상해보십시오. 이제 스레드 A와 B는 tbl_RegistrationCertificates에 동일한 값을 INSERT하려고합니다.

SCOPE_IDENTITY() 함수는이 저장 프로 시저 내의 쿼리에서 생성 된 최신 ID 값을 제공하여이 작업에 도움이됩니다.

일부 업데이트 된 코드 :

BEGIN TRANSACTION 
INSERT INTO tbl_Registrations ... 

DECLARE @RID int; 
--get newly-created registration ID 
SELECT @RID = SCOPE_IDENTITY() 
INSERT INTO tbl_RegistrationCertificates VALUES (@RID, 9); 
COMMIT TRANSACTION 
+0

아,'SCOPE_IDENTITY()는'내가 전에 본 적이 일이 아니다; 그것은 매우 도움이됩니다. 명백히 간결함을 위해 생략 한 많은 코드가 있었지만 두 번째 INSERT는 SP에 대한 매개 변수에 따라 다른 기준이 충족되면 실행됩니다. 이 경우 거래가 여전히 의미가 있습니까? –

+0

또 하나의 질문 : 거래와 배치 분리 기호를 사용하는 것의 차이점은 무엇입니까? 'GO' 대'BEGIN TRANSACTION ... COMMIT TRANSACTION'? –

+1

트랜잭션은 논리적으로 여러 문을 그룹화하여 성공하거나 실패한 문을 그룹화합니다. 위의 코드에서 어떤 이유로 인해 _tbl_RegistrationCertificates_에 대한 INSERT가 실패하면 _tbl_Registrations_에 대한 INSERT도 롤백됩니다. 트랜잭션은 하나의 명령문 그룹이 모두 하나의 연산 (BEGIN과 COMMIT 사이의 모든 것)으로 처리되어야하는 논리적으로 일관된 상태를 유지하도록 도와줍니다. 반면에'GO '는 SQL Server 도구에 일련의 명령문을 실행하도록 지시합니다. –