2010-11-18 5 views
5

다음과 같은 SQL을 고려 값 :마십시오 삽입 된 기록은 항상 연속 신원을 받기는

CREATE TABLE Foo 
(
    ID int IDENTITY(1,1), 
    Data nvarchar(max) 
) 

INSERT INTO Foo (Data) 
SELECT TOP 1000 Data 
FROM SomeOtherTable 
WHERE SomeColumn = @SomeParameter 

DECLARE @LastID int 
SET @LastID = SCOPE_IDENTITY() 
나는 내가 테이블 푸 연속 ID 값을 가진에 삽입 된 1000 행에 의존 할 수 있는지 알고 싶습니다

. 즉,이 SQL 블록이 @LastID 2000을 생성하는 경우, 내가 삽입 한 첫 번째 레코드의 ID가 1001이라는 것을 확실히 알 수 있습니까? 나는 주로 테이블 Foo에 레코드를 동시에 삽입하는 다중 명령문에 대해 궁금합니다.

내가 원하는 동작을 보장하기 위해 삽입 문 주위에 serializable 트랜잭션을 추가 할 수 있다는 것을 알고 있지만 실제로 필요합니까? 직렬화가 가능한 트랜잭션을 도입하면 성능이 저하되지만, SQL 문에서이 명령문이 실행되는 동안 SQL Server가 다른 명령문을 테이블 Foo에 삽입 할 수 없다면 걱정할 필요가 없습니다.

답변

7

나는 답변에 동의하지 않습니다. 다음을 실행하면 쉽게 테스트하고 반증 할 수 있습니다.

설정

USE tempdb 

CREATE TABLE Foo 
(
    ID int IDENTITY(1,1), 
    Data nvarchar(max) 
) 

연결 한

USE tempdb 

SET NOCOUNT ON 
WHILE NOT EXISTS(SELECT * FROM master..sysprocesses WHERE context_info = CAST('stop' AS VARBINARY(128))) 
BEGIN 
INSERT INTO Foo (Data) 
VALUES ('blah') 
END 

연결이

USE tempdb 

SET NOCOUNT ON 
SET CONTEXT_INFO 0x 

DECLARE @Output TABLE(ID INT) 

WHILE 1 = 1 
BEGIN 
    /*Clear out table variable from previous loop*/ 
    DELETE FROM @Output 

    /*Insert 1000 records*/ 
    INSERT INTO Foo (Data) 
    OUTPUT inserted.ID INTO @Output 
    SELECT TOP 1000 NEWID() 
    FROM sys.all_columns 

    IF EXISTS(SELECT * FROM @Output HAVING MAX(ID) - MIN(ID) <> 999) 
     BEGIN 
     /*Set Context Info so other connection inserting 
      a single record in a loop terminates itself*/ 
     DECLARE @stop VARBINARY(128) 
     SET @stop = CAST('stop' AS VARBINARY(128)) 
     SET CONTEXT_INFO @stop 

     /*Return results for inspection*/ 
     SELECT ID, DENSE_RANK() OVER (ORDER BY Grp) AS ContigSection 
     FROM 
      (SELECT ID, ID - ROW_NUMBER() OVER (ORDER BY [ID]) AS Grp 
      FROM @Output) O 
     ORDER BY ID 

     RETURN 
     END 
END 
+0

매우 흥미로운 발견! 이 시험을 함께 해 주셔서 감사합니다. 나는 "1000 레코드 삽입"문장을 직렬화 가능 트랜잭션으로 래핑하려고했지만 아무 소용이 없어도 여전히 인터리브 된 레코드를 얻습니다. 그러나 "Insert 1000 records"를 직렬화 가능 트랜잭션에 넣은 다음 삽입하기 전에 트랜잭션 내에서 SELECT MAX (ID) FROM FROM을 호출하면 내 레코드가 연속적임을 보장 할 수 있습니다. 이 솔루션은 동시 삽입을 방지합니다. 성능에 영향을 줄 수는 없습니다. –

+1

@ 존 - 힙에 직렬화 할 수있는 것은 독점적 인 테이블 잠금을 획득 할 것이므로 직선적 인 잠금 힌트로 동일한 효과를 얻을 수 있다고 생각합니다. 'WITH (TABLOCKX)'이것은 동시성에 영향을 미칠 것이라고 말했듯이. –

+0

내 실수, 테스트에서 나는 테이블 Foo의 ID 컬럼에 PK를 추가했다. –

6

예, INSERT가 atomic : complete success 또는 full rollback이므로 인접 해 있습니다. 그것은 또한 하나의 작업 단위로 수행됩니다 : 당신이 원하는 경우에 당신은 늘 다른 프로세스

어떤 "인터리빙"을 얻을 는

그러나 (! 또는 나머지에 당신의 마음을 넣어)에 OUTPUT clause

DECLARE @KeyStore TABLE (ID int NOT NULL) 

INSERT INTO Foo (Data) 
OUTPUT INSERTED.ID INTO @KeyStore (ID) --this line 
SELECT TOP 1000 Data 
FROM SomeOtherTable 
WHERE SomeColumn = @SomeParameter 
+0

+1 동의 -이 트랜잭션 내에서 그들이 연속 될 것입니다 -하지만 테이블의 수명에, 보장이 전혀 이루어지지 않습니다 - 격차 –

+0

죄송가있을 수 있습니다,하지만 난 @ 마틴의 테스트를 통해 실행 및 실제로 레코드가 삽입 도중에 테이블로 인터리브 될 수 있음을 보여줍니다. INSERT가 원자라는 주장은 여전히 ​​유효하지만 행이 삽입 될 수있는 것은 아닙니다. –

+0

당신의 답변이 연속적인 부분에서 잘못 되었음에도 불구하고, 나는 OUTPUT ... INTO를 사용하여 당신의 문제와 @ KM의 제안을 해결했습니다. –

3

을 고려 여러 행에 대한 ID 값은 OUTPUT을 사용합니다.

DECLARE @NewIDs table (PKColumn int) 
INSERT INTO Foo (Data) 
    OUTPUT INSERTED.PKColumn 
    INTO @NewIDs 
SELECT TOP 1000 Data 
FROM SomeOtherTable 
WHERE SomeColumn = @SomeParameter 

이제 @NewIDs 테이블에 전체 값 집합이 있습니다. Foo 테이블의 모든 열을 @NewIDs 테이블에 추가하고 해당 열을 삽입 할 수도 있습니다.

1

신원 값에 어떤 의미 가든 첨부하는 것은 좋지 않습니다. 테이블의 범위 내에서 고유 한 것으로 보장 된 정수 이상이라고 가정해야합니다.

+0

나는 이론 상으로는 당신과 동의하는 경향이 있지만, 실제로 이것은 문제가되지 않을 수도 있습니다. 개인적으로 저는보다 견고한 솔루션을 모색 할 것입니다. – MikeAinOz

0

봅니다 다음 추가 :

option(maxdop 1)

+1

이 옵션이 설정된 멀티 코어 시스템에서 @ Martin의 테스트를 실행 했습니까? 결과를 듣고 싶습니다. –

관련 문제