获取插入行的标识的最佳方法是什么?

时间:2008-09-03 21:32:03

标签: sql sql-server tsql

获取IDENTITY插入行的最佳方法是什么?

我了解@@IDENTITYIDENT_CURRENT以及SCOPE_IDENTITY,但不了解每个人的利弊。

有人可以解释一下这些差异吗?我应该在何时使用它们?

15 个答案:

答案 0 :(得分:1287)

  • @@IDENTITY返回在所有范围内为当前会话中的任何表生成的最后一个标识值。 这里需要小心,因为它跨越范围。您可以从触发器获取值,而不是当前的语句。

  • SCOPE_IDENTITY()返回为当前会话中的任何表和当前范围生成的最后一个标识值。 一般您想要使用

  • IDENT_CURRENT('tableName')返回在任何会话和任何范围内为特定表生成的最后一个标识值。这使您可以指定您想要该值的表格,以防上述两个表格不是您需要的(非常罕见)。此外,正如@ Guy Starbuck所提到的,“如果您想获取未插入记录的表的当前IDENTITY值,则可以使用此值。”

  • INSERT语句的OUTPUT clause将允许您访问通过该语句插入的每一行。由于它的范围是特定的声明,它比上面的其他函数更直接。但是,它有点更详细(您需要插入表变量/临时表然后查询)并且即使在回滚语句的错误情况下也会给出结果。也就是说,如果您的查询使用并行执行计划,这是获取身份的唯一保证的方法(缺少关闭并行性)。但是,它在触发器之前执行,不能用于返回触发器生成的值。

答案 1 :(得分:165)

我相信检索插入的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

答案 2 :(得分:102)

我说的和其他人一样,所以每个人都是对的,我只是想让它更清楚。

@@IDENTITY返回客户端与数据库连接所插入的最后一个内容的id 大部分时间这种方法都可以正常工作,但有时触发器会插入一个你不知道的新行,你将从这个新行中获取ID,而不是你想要的那个

SCOPE_IDENTITY()解决了这个问题。它返回您插入的 在您发送的 到数据库的最后一件事的ID。如果触发器去创建额外的行,它们将不会导致返回错误的值。万岁

IDENT_CURRENT返回任何人插入的最后一个ID。如果某个其他应用程序恰好在不合适的时间插入另一行,您将获得该行的ID而不是您的ID。

如果您想安全地玩,请始终使用SCOPE_IDENTITY()。如果您坚持使用@@IDENTITY并且有人决定稍后添加触发器,那么您的所有代码都将中断。

答案 3 :(得分:62)

获取新插入行的标识的最佳(读取:最安全)方法是使用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)

答案 4 :(得分:21)

添加

SELECT CAST(scope_identity() AS int);

到insert sql语句的末尾,然后

NewId = command.ExecuteScalar()

将检索它。

答案 5 :(得分:14)

MSDN

  

@@ IDENTITY,SCOPE_IDENTITY和IDENT_CURRENT是类似的函数,因为它们返回插入表的IDENTITY列的最后一个值。

     

@@ IDENTITY和SCOPE_IDENTITY将返回当前会话中任何表中生成的最后一个标识值。但是,SCOPE_IDENTITY仅在当前范围内返回值; @@ IDENTITY不限于特定范围。

     

IDENT_CURRENT不受范围和会话的限制;它仅限于指定的表格。 IDENT_CURRENT返回在任何会话和任何范围内为特定表生成的标识值。有关更多信息,请参阅IDENT_CURRENT。

答案 6 :(得分:13)

@@ IDENTITY 是使用当前SQL连接插入的最后一个标识。这是从插入存储过程返回的一个很好的值,您只需要为新记录插入标识,并且不关心之后是否添加了更多行。

SCOPE_IDENTITY 是使用当前SQL连接插入的最后一个标识,并且在当前范围内 - 也就是说,如果在插入后基于触发器插入了第二个IDENTITY,则不会反映在SCOPE_IDENTITY中,只反映您执行的插入。坦率地说,我从来没有理由使用它。

IDENT_CURRENT(tablename)是插入的最后一个标识,与连接或范围无关。如果要获取未插入记录的表的当前IDENTITY值,可以使用此方法。

答案 7 :(得分:13)

当您使用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或更高版本。

答案 8 :(得分:10)

我无法与其他版本的SQL Server对话,但在2012年,直接输出效果很好。你不需要打扰临时表。

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

顺便说一下,这种技术在插入多行时也有效。

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

输出

ID
2
3
4

答案 9 :(得分:8)

总是使用scope_identity(),不需要任何其他内容。

答案 10 :(得分:1)

保证插入行的身份的另一种方法是指定身份值,并依次使用SET IDENTITY_INSERT ONOFF。这样可以保证您确切知道身份值是什么!只要不使用这些值,就可以将这些值插入到Identity列中。

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 

如果要从另一个源加载数据或合并两个数据库等的数据,这可能是非常有用的技术。

答案 11 :(得分:1)

即使这是一个较旧的线程,也有一种更新的方法可以避免旧版本的SQL Server like gaps in the identity values after server reboots中IDENTITY列的某些陷阱。序列在SQL Server 2016中可用,并且向前更新,这是较新的方法是使用TSQL创建SEQUENCE对象。这使您可以在SQL Server中创建自己的数字序列对象并控制其递增方式。

这里是一个示例:

month

然后在TSQL中,您将执行以下操作以获得下一个序列ID:

CREATE SEQUENCE CountBy1  
    START WITH 1  
    INCREMENT BY 1 ;  
GO  

以下是指向CREATE SEQUENCENEXT VALUE FOR的链接

答案 12 :(得分:0)

在插入语句后,您需要添加此项。并确保数据插入的表名。您的插入语句刚才会影响当前行的行。

IDENT_CURRENT('tableName')

答案 13 :(得分:0)

如果您正在寻找添加/更新的最后一个ID,这可能有点老套了,但是有很多人在使用较旧的PHP,Pre 5.5或更精确。更多详细信息,请访问http://php.net/manual/en/function.mysql-insert-id.php

$last = mysql_insert_id();

答案 14 :(得分:-1)

创建一个 function = () => { const { saveValue } = this.props; this is a async action. saveValues().then(() => { console.log('Success'); }).catch(() => { console.log('Error'); }); }; 并将其插入到列中。然后,您可以使用uuid轻松识别您的行。