삽입 된 행의 정체성을 얻는 가장 좋은 방법은 무엇입니까?


1119

IDENTITY삽입 된 행 을 얻는 가장 좋은 방법은 무엇입니까 ?

내가 알고 @@IDENTITY하고 IDENT_CURRENT하고 SCOPE_IDENTITY있지만 각에 부착 된 장점과 단점을 이해하지 않습니다.

누군가 차이점과 언제 사용해야하는지 설명해 주시겠습니까?


5
INSERT INTO Table1(fields...) OUTPUT INSERTED.id VALUES (...)또는 이전 방법 : INSERT INTO Table1(fields...) VALUES (...); SELECT SCOPE_IDENTITY();ExecuteScalar ()를 사용하여 c #으로 가져올 수 있습니다.
S.Serpooshan

4
다른 답변보다 어떻게 더 좋습니까? (또한-왜 주석 대신 답변으로 이것을 게시하지 않습니까?) 완전한 답변을 작성하십시오 (그리고 이것이 게시 된 것보다 왜 더 나은 옵션인지 설명하십시오-버전에 따라 다르면 말하십시오).
Oded

짧은 요약과 같습니다. ; D 허용되는 답변에 OUTPUT 절 구문이 언급되어 있지 않으며 샘플이 없습니다. 또한 다른 게시물의 샘플은 그렇게 깨끗하지 않습니다 ...
S.Serpooshan

2
@saeedserpooshan-그런 다음 편집하십시오. 알 수 있습니까? 답변이 게시 된시기를 참조하세요? 이는 OUTPUTSQL Server 의 절 보다 우선합니다 .
Oded

답변:


1434
  • @@IDENTITY모든 범위에서 현재 세션의 테이블에 대해 생성 된 마지막 ID 값을 반환합니다. 범위가 다르므로 여기서주의해야합니다 . 현재 명령문 대신 트리거에서 값을 얻을 수 있습니다.

  • SCOPE_IDENTITY()현재 세션 및 현재 범위의 테이블에 대해 생성 된 마지막 ID 값을 반환합니다. 일반적으로 사용하고 싶은 것 .

  • IDENT_CURRENT('tableName')모든 세션 및 범위에서 특정 테이블에 대해 생성 된 마지막 ID 값을 반환합니다. 이것은 위의 두 가지가 당신이 필요로하지 않는 경우 ( 매우 드문 경우 ) 값을 원하는 테이블을 지정할 수있게합니다 . 또한 @ Guy Starbuck이 언급했듯이 "레코드를 삽입하지 않은 테이블의 현재 IDENTITY 값을 얻으려는 경우이를 사용할 수 있습니다."

  • 명령문 의 OUTPUTINSERT통해 해당 명령문을 통해 삽입 된 모든 행에 액세스 할 수 있습니다. 특정 문장에 적용되므로 위의 다른 기능보다 더 간단 합니다. 그러나 조금 더 장황하고 (테이블 변수 / 임시 테이블에 삽입 한 다음 쿼리해야 함), 명령문이 롤백되는 오류 시나리오에서도 결과를 제공합니다. 즉, 쿼리에서 병렬 실행 계획을 사용하는 경우 이것이 병렬 처리를 해제하지 않는 ID를 얻는 유일한 방법 입니다. 그러나 트리거 전에 실행되며 트리거 생성 값을 반환하는 데 사용할 수 없습니다.


48
SCOPE_IDENTITY ()에서 잘못된 값을 반환하는 알려진 버그 : blog.sqlauthority.com/2009/03/24/… 해결 방법은 다중 프로세서 병렬 계획에서 INSERT를 실행하지 않거나 OUTPUT 절
KM을

3
거의 '정체성'을 원할 때마다 방금 삽입 한 레코드의 키를 알고 싶었습니다. 그런 상황이라면 OUTPUT 절을 사용하려고합니다. 다른 것을 원한다면 bdukes 응답을 읽고 이해하기 위해 노력하십시오.
jerry

3
으로 output당신은 결과를 저장하기 위해 임시 테이블을 생성하고 쿼리 할 필요가 없습니다. 그냥 생략 할 into출력 조항의 일부를 그리고 그것은 결과 집합을 출력 할 것이다.
spb

96
패닉을 피하기 위해 위에서 언급 한 버그가 SQL Server 2008 R2 서비스 팩 1 용 누적 업데이트 5에서 수정되었습니다.
GaTechThomas

1
@niico, 나는 추천이 그랬던 것과 같다고 생각한다. 이것은 OUTPUT트리거를 사용하지 않고 에러를 처리하는 한 "최고"이지만 SCOPE_IDENTITY가장 간단하고 거의 문제가 없다
bdukes

180

삽입 된 ID를 검색하는 가장 안전하고 정확한 방법은 출력 절을 사용하는 것입니다.

예를 들어 (다음 MSDN 기사 에서 발췌 )

USE AdventureWorks2008R2;
GO
DECLARE @MyTableVar table( NewScrapReasonID smallint,
                           Name varchar(50),
                           ModifiedDate datetime);
INSERT Production.ScrapReason
    OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate
        INTO @MyTableVar
VALUES (N'Operator error', GETDATE());

--Display the result set of the table variable.
SELECT NewScrapReasonID, Name, ModifiedDate FROM @MyTableVar;
--Display the result set of the table.
SELECT ScrapReasonID, Name, ModifiedDate 
FROM Production.ScrapReason;
GO

3
예, 이것은 올바른 방법입니다. SQL Server 2008을 사용하지 않는 경우 다른 방법 중 하나만 사용하십시오 (2005 년을 건너 뛰었으므로 OUTPUT을 사용할 수 있는지 확실하지 않습니다)
HLGEM

1
@HLGEM는 거기 에 대한 MSDN 페이지 OUTPUTSQL 서버 2005 좋아 보이는 이전에 2000 만 SQL 서버이고, 그래서 그것을하지 않고있다가,
bdukes

6
우후! OUTPUT CLAUSE rock :) 현재 작업을 단순화합니다. 전에 그 말을 몰랐다. 감사합니다!
SwissCoder

8
삽입 된 ID를 얻는 간결한 예제를 보려면 stackoverflow.com/a/10999467/2003325
Luke

OUTPUT과 함께 INTO를 사용하는 것이 좋습니다. 참조 : blogs.msdn.microsoft.com/sqlprogrammability/2008/07/11/... (여기에 코멘트에서 : stackoverflow.com/questions/7917695/... )
shlgug

112

나는 다른 사람들과 같은 말을하고 있기 때문에 모든 사람들이 맞습니다. 나는 그것을 더 명확하게하려고합니다.

@@IDENTITY클라이언트의 데이터베이스 연결에 의해 마지막으로 삽입 된 ID를 반환합니다.
대부분의 경우 정상적으로 작동하지만 때로는 트리거가 발생하여 모르는 새 행을 삽입하고 원하는 새 행 대신이 새 행에서 ID를 얻습니다.

SCOPE_IDENTITY()이 문제를 해결합니다. 데이터베이스에 보낸 SQL 코드에 마지막으로 삽입 한 ID를 반환 합니다. 트리거가 이동하여 추가 행을 작성하면 잘못된 값이 리턴되지 않습니다. 후 레이

IDENT_CURRENT누구나 삽입 한 마지막 ID를 반환합니다. 다른 응용 프로그램이 예기치 않은 시간에 다른 행을 삽입하면 해당 행 대신 ID가 표시됩니다.

안전하게 플레이하려면 항상을 사용하십시오 SCOPE_IDENTITY(). 고수 @@IDENTITY하고 누군가가 나중에 트리거를 추가하기로 결정하면 모든 코드가 중단됩니다.


64

새로 삽입 된 행의 ID를 얻는 가장 좋은 (읽기 : 가장 안전한) 방법은 다음 output절 을 사용하는 것입니다 .

create table TableWithIdentity
           ( IdentityColumnName int identity(1, 1) not null primary key,
             ... )

-- type of this table's column must match the type of the
-- identity column of the table you'll be inserting into
declare @IdentityOutput table ( ID int )

insert TableWithIdentity
     ( ... )
output inserted.IdentityColumnName into @IdentityOutput
values
     ( ... )

select @IdentityValue = (select ID from @IdentityOutput)

5
SQL Server 클러스터링은 고 가용성 기능이며 병렬 처리와 관련이 없습니다. 단일 행 삽입 (가장 일반적인 경우 scope_identity())이 병렬 계획을 얻는 것은 매우 드문 일 입니다. 그리고이 버그는이 답변보다 1 년 이상 전에 수정되었습니다.
Martin Smith

병렬 처리 란 무엇을 의미합니까?
user1451111

@MartinSmith 클라이언트는이 문제를 해결하는 CU를 설치하기 위해 서버 클러스터에서 가동 중지 시간을 기꺼이 허용하지 않았습니다 (농담 아님) . output대신 사용할 모든 SQL을 다시 작성하는 것이 유일한 해결책이었습니다 scope_identity(). 답변에서 클러스터링에 대한 FUD를 제거했습니다.
Ian Kemp

1
고맙습니다. 이것은 내가 출력 한 값 대신 변수에서 출력 값을 사용하는 방법을 보여주는 유일한 예입니다.
Sean Ray

26

더하다

SELECT CAST(scope_identity() AS int);

insert sql 문의 끝까지

NewId = command.ExecuteScalar()

그것을 검색합니다.


18

Entity Framework를 사용하면 내부적으로이 OUTPUT기술을 사용 하여 새로 삽입 된 ID 값을 반환합니다.

DECLARE @generated_keys table([Id] uniqueidentifier)

INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO @generated_keys
VALUES('Malleable logarithmic casing');

SELECT t.[TurboEncabulatorID ]
FROM @generated_keys AS g 
   JOIN dbo.TurboEncabulators AS t 
   ON g.Id = t.TurboEncabulatorID 
WHERE @@ROWCOUNT > 0

출력 결과는 임시 테이블 변수에 저장되고 테이블에 다시 결합되고 테이블에서 행 값을 리턴합니다.

참고 : 왜 EF가 임시 테이블을 실제 테이블로 다시 조인하는지 알 수 없습니다 (두 가지가 일치하지 않는 상황).

그러나 이것이 EF가하는 일입니다.

이 기술 ( OUTPUT)은 SQL Server 2008 이상에서만 사용할 수 있습니다.

편집 -가입 이유

Entity Framework가 단순히 OUTPUT값을 사용하는 대신 원래 테이블로 다시 조인하는 이유 는 EF가이 기술을 사용 rowversion하여 새로 삽입 된 행 을 가져 오기 때문 입니다.

다음과 같은 방법으로 당신의 엔티티 프레임 워크 모델에서 낙관적 동시성을 사용할 수 있습니다 사용하여 Timestamp속성을 : 🕗

public class TurboEncabulator
{
   public String StatorSlots)

   [Timestamp]
   public byte[] RowVersion { get; set; }
}

이렇게하면 Entity Framework rowversion에 새로 삽입 된 행 이 필요합니다 .

DECLARE @generated_keys table([Id] uniqueidentifier)

INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO @generated_keys
VALUES('Malleable logarithmic casing');

SELECT t.[TurboEncabulatorID], t.[RowVersion]
FROM @generated_keys AS g 
   JOIN dbo.TurboEncabulators AS t 
   ON g.Id = t.TurboEncabulatorID 
WHERE @@ROWCOUNT > 0

그리고이를 검색하기 위해 Timetsamp당신이 할 수 사용 OUTPUT절을.

테이블에 트리거가 Timestamp있으면 OUTPUT이 잘못되기 때문입니다.

  • 초기 삽입. 타임 스탬프 : 1
  • OUTPUT 절 출력 타임 스탬프 : 1
  • 트리거는 행을 수정합니다. 타임 스탬프 : 2

테이블에 트리거가 있으면 반환 된 타임 스탬프가 정확 하지 않습니다 . 따라서 별도 의을 사용해야합니다 SELECT.

그리고 잘못된 행 버전을 기꺼이 겪을지라도, 분리를 수행하는 다른 이유 는 테이블 변수에 SELECTa rowversion를 출력 할 수 없기 때문입니다 .

DECLARE @generated_keys table([Id] uniqueidentifier, [Rowversion] timestamp)

INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID, inserted.Rowversion INTO @generated_keys
VALUES('Malleable logarithmic casing');

세 번째 이유는 대칭 때문입니다. UPDATE트리거가있는 테이블에서 수행 할 때는 절을 사용할 수 없습니다OUTPUT . 로 시도 UPDATE하는 OUTPUT것은 지원되지 않으며 오류가 발생합니다.

이를 수행하는 유일한 방법은 후속 SELECT진술을 사용하는 것입니다.

UPDATE TurboEncabulators
SET StatorSlots = 'Lotus-O deltoid type'
WHERE ((TurboEncabulatorID = 1) AND (RowVersion = 792))

SELECT RowVersion
FROM TurboEncabulators
WHERE @@ROWCOUNT > 0 AND TurboEncabulatorID = 1

2
무결성을 보장하기 위해 일치한다고 생각합니다 (예 : 낙관적 동시성 모드에서 테이블 변수에서 선택하는 동안 누군가가 삽입 기 행을 제거했을 수 있습니다). 또한, 당신을 사랑하십시오 TurboEncabulators:)
zaitsman

16

MSDN

@@ IDENTITY, SCOPE_IDENTITY 및 IDENT_CURRENT는 테이블의 IDENTITY 열에 삽입 된 마지막 값을 반환한다는 점에서 비슷한 함수입니다.

@@ IDENTITY 및 SCOPE_IDENTITY는 현재 세션의 모든 테이블에서 생성 된 마지막 ID 값을 반환합니다. 그러나 SCOPE_IDENTITY는 현재 범위 내에서만 값을 반환합니다. @@ IDENTITY는 특정 범위로 제한되지 않습니다.

IDENT_CURRENT는 범위와 세션에 의해 제한되지 않습니다. 지정된 테이블로 제한됩니다. IDENT_CURRENT는 모든 세션 및 범위의 특정 테이블에 대해 생성 된 ID 값을 반환합니다. 자세한 내용은 IDENT_CURRENT를 참조하십시오.

  • IDENT_CURRENT 는 테이블을 인수로 취하는 함수입니다.
  • 테이블에 트리거가 있으면 @@ IDENTITY 가 혼란스러운 결과를 반환 할 수 있습니다.
  • SCOPE_IDENTITY 는 대부분의 영웅입니다.

14

@@ IDENTITY 는 현재 SQL 연결을 사용하여 삽입 된 마지막 ID입니다. 이것은 삽입 저장 프로 시저에서 반환하기에 좋은 값입니다. 여기서 새 레코드에 삽입 된 ID 만 있으면되고 이후에 더 많은 행이 추가되었는지는 신경 쓰지 않습니다.

SCOPE_IDENTITY 는 현재 SQL 연결을 사용하여 삽입 된 마지막 ID이며 현재 범위에서, 즉 삽입 후 트리거를 기반으로 두 번째 IDENTITY가 삽입 된 경우 SCOPE_IDENTITY에 반영되지 않으며 수행 한 삽입 만 반영됩니다 . 솔직히, 나는 이것을 사용할 이유가 없었습니다.

IDENT_CURRENT (tablename) 은 연결 또는 범위에 관계없이 삽입 된 마지막 ID입니다. 레코드를 삽입하지 않은 테이블의 현재 IDENTITY 값을 가져 오려는 경우이를 사용할 수 있습니다.


2
이 목적으로 @@ identity를 사용해서는 안됩니다. 누군가 나중에 트리거를 추가하면 데이터 무결성이 손실됩니다. @@ identiy는 매우 위험한 방법입니다.
HLGEM

1
"레코드를 << not >> 삽입 한 테이블의 값" 정말?
Abdul Saboor

13

다른 버전의 SQL Server와 대화 할 수는 없지만 2012 년에는 직접 출력하는 것이 좋습니다. 임시 테이블을 신경 쓸 필요가 없습니다.

INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES (...)

그런데이 기술은 여러 행을 삽입 할 때도 작동합니다.

INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES
    (...),
    (...),
    (...)

산출

ID
2
3
4

나중에하지만 그것을 사용하려면, 당신은 임시 테이블이 필요 상상
JohnOsborne

@JohnOsborne 원하는 경우 임시 테이블을 사용할 수 있지만 내 요점은의 요구 사항이 아니라는 것입니다 OUTPUT. 임시 테이블이 필요하지 않으면 쿼리가 훨씬 간단 해집니다.
MarredCheese

10

항상 scope_identity ()를 사용하면 다른 어떤 것도 필요하지 않습니다.


13
그렇진 않습니다 하지만 100 점 만점에 99 시간, 당신이 SCOPE_IDENTITY를 사용하지 않는 것 ().
CJM

다른 어떤 것을 사용해 본 적이 있습니까?
erikkallen

11
INSERT-SELECT를 사용하여 여러 행을 삽입하는 경우 OUTPUT 절
KM을

1
@ KM : 예,하지만 scope_identity vs @@ identity vs ident_current를 참조했습니다. OUTPUT은 완전히 다른 클래스이며 종종 유용합니다.
erikkallen

2
이 질문에 대한 Orry 's ( stackoverflow.com/a/6073578/2440976 )의 답변을 확인하십시오 -병렬 처리로, 모범 사례와 마찬가지로 그의 설정을 따르는 것이 현명합니다 ... 훌륭합니다!
Dan B

2

uuid만들고 열에 삽입하십시오. 그러면 UUID로 행을 쉽게 식별 할 수 있습니다. 이것이 100 % 작동하는 유일한 솔루션입니다. 다른 모든 솔루션이 너무 복잡하거나 동일한 경우에 작동하지 않습니다. 예 :

1) 행 만들기

INSERT INTO table (uuid, name, street, zip) 
        VALUES ('2f802845-447b-4caa-8783-2086a0a8d437', 'Peter', 'Mainstreet 7', '88888');

2) 행 생성

SELECT * FROM table WHERE uuid='2f802845-447b-4caa-8783-2086a0a8d437';

uuid데이터베이스에서에 대한 색인을 작성하는 것을 잊지 마십시오 . 따라서 행이 더 빨리 발견됩니다.
Frank Roth

node.js의 경우이 모듈을 사용하여 단순히 uuid를 만들 수 있습니다 https://www.npmjs.com/package/uuid. const uuidv4 = require('uuid/v4'); const uuid = uuidv4()
Frank Roth

GUID는 ID 값이 아니며 단순한 정수와 비교하여 약간의 백 드로우가 있습니다.
Alejandro

1

행의 신원을 보장하는 또 다른 방법은 삽입 신원 값을 지정하고를 사용하는 SET IDENTITY_INSERT ON다음과 OFF. 이를 통해 ID 값이 무엇인지 정확하게 알 수 있습니다! 값을 사용하지 않는 한이 값을 ID 열에 삽입 할 수 있습니다.

CREATE TABLE #foo 
  ( 
     fooid   INT IDENTITY NOT NULL, 
     fooname VARCHAR(20) 
  ) 

SELECT @@Identity            AS [@@Identity], 
       Scope_identity()      AS [SCOPE_IDENTITY()], 
       Ident_current('#Foo') AS [IDENT_CURRENT] 

SET IDENTITY_INSERT #foo ON 

INSERT INTO #foo 
            (fooid, 
             fooname) 
VALUES      (1, 
             'one'), 
            (2, 
             'Two') 

SET IDENTITY_INSERT #foo OFF 

SELECT @@Identity            AS [@@Identity], 
       Scope_identity()      AS [SCOPE_IDENTITY()], 
       Ident_current('#Foo') AS [IDENT_CURRENT] 

INSERT INTO #foo 
            (fooname) 
VALUES      ('Three') 

SELECT @@Identity            AS [@@Identity], 
       Scope_identity()      AS [SCOPE_IDENTITY()], 
       Ident_current('#Foo') AS [IDENT_CURRENT] 

-- YOU CAN INSERT  
SET IDENTITY_INSERT #foo ON 

INSERT INTO #foo 
            (fooid, 
             fooname) 
VALUES      (10, 
             'Ten'), 
            (11, 
             'Eleven') 

SET IDENTITY_INSERT #foo OFF 

SELECT @@Identity            AS [@@Identity], 
       Scope_identity()      AS [SCOPE_IDENTITY()], 
       Ident_current('#Foo') AS [IDENT_CURRENT] 

SELECT * 
FROM   #foo 

이것은 다른 소스에서 데이터를로드하거나 두 데이터베이스의 데이터를 병합하는 경우 매우 유용한 기술입니다.


0

이것이 이전 스레드이지만 서버를 재부팅 한 후 ID 값의 간격과 같이 이전 버전의 SQL Server에서 IDENTITY 열의 함정을 피하는 새로운 방법 있습니다. SQL Server 2016부터 시퀀스를 사용할 수 있으며 TSQL을 사용하여 SEQUENCE 개체를 만드는 가장 새로운 방법입니다. 이를 통해 SQL Server에서 고유 한 숫자 시퀀스 개체를 만들고 증가 방법을 제어 할 수 있습니다.

예를 들면 다음과 같습니다.

CREATE SEQUENCE CountBy1  
    START WITH 1  
    INCREMENT BY 1 ;  
GO  

그런 다음 TSQL에서 다음 순서 ID를 얻기 위해 다음을 수행합니다.

SELECT NEXT VALUE FOR CountBy1 AS SequenceID
GO

CREATE SEQUENCENEXT VALUE FOR 링크는 다음과 같습니다.


시퀀스는 격차 (실제로는 문제가되지 않음)와 같은 동일성 문제가 있습니다.
Alejandro

-1

Insert Statement 다음에 이것을 추가해야합니다. 그리고 데이터가 삽입되는 테이블 이름을 확인하십시오. 지금 삽입 행의 영향을받는 행이 현재 행에 표시되지 않습니다.

IDENT_CURRENT('tableName')

2
이 같은 제안이 여러 번 전에 답변 된 것을 보셨습니까?
TT.

예. 그러나 나는 내 방식으로 솔루션을 설명하려고합니다.
칸 Ataur 라만

그리고 다른 사람이 insert 문과 IDENT_CURRENT () 호출 사이에 행을 삽입하면 다른 사람이 삽입 한 레코드의 ID를 얻을 수 있습니다. 위의 대부분의 답글에서 언급했듯이 대부분의 경우 SCOPE_IDENTITY ()를 사용해야합니다.
Trondster
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.