如何获取插入行的标识?

我应该如何获得插入行的IDENTITY

我知道@@IDENTITYIDENT_CURRENTSCOPE_IDENTITY,但不明白它们各自的含义或影响。

有人能解释一下差异以及我什么时候会使用它们吗?

1117637 次浏览

从msdn

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

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

IDENT_CURRENT不受作用域和会话的限制;它仅限于指定的表。IDENT_CURRENT返回为任何会话和任何作用域中的特定表生成的标识值。有关详细信息,请参阅IDENT_CURRENT。

  • IDENT_CURRENT是一个以表为参数的函数。
  • 当您在表上有触发器时,@@IDENTITY可能会返回令人困惑的结果
  • SCOPE_IDENTITY大部分时间都是你的英雄。
  • @@IDENTITY返回当前会话中为所有范围内的任何表生成的最后一个标识值。

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

  • IDENT_CURRENT('tableName')返回在任何会话和任何作用域中为特定表生成的最后一个标识值。这允许您指定要从哪个表中获取值,以防上面两个不是您需要的(非常罕见)。此外,正如@Guy Starbuck提到的,“如果您想获取尚未插入记录的表的当前IDENTITY值,您可以使用它。”

  • INSERT语句的OUTPUT子句将允许您访问通过该语句插入的每一行。由于它的作用域是特定的语句,因此它比上面的其他函数更简单。然而,它有点更冗长(你需要插入到表变量/temp表中,然后查询它),即使在语句回滚的错误场景中它也会给出结果。也就是说,如果你的查询使用并行执行计划,这是获取身份的唯一保证方法(除了关闭并行)。但是,它在触发器之前执行,不能用于返回触发器生成的值。

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

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

IDENT_CURRENT(表名)是最后插入的标识,无论连接或范围如何。如果您想获取尚未插入记录的表的当前IDENTITY值,您可以使用它。

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

@@IDENTITY返回客户端与数据库的连接插入的最后一个内容的ID。
大多数情况下,这工作得很好,但有时触发器会插入一个你不知道的新行,你会从这个新行获得ID,而不是你想要的那个。

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

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

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

一直都是使用scope_identity(),永远不需要其他任何东西。

我相信检索插入的id的最安全和最准确的方法是使用输出子句。

例如(摘自以下MSDN文章)

USE AdventureWorks2008R2;GODECLARE @MyTableVar table( NewScrapReasonID smallint,Name varchar(50),ModifiedDate datetime);INSERT Production.ScrapReasonOUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDateINTO @MyTableVarVALUES (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, ModifiedDateFROM Production.ScrapReason;GO

获取新插入行标识的最佳(阅读:最安全)方法是使用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 intodeclare @IdentityOutput table ( ID int )
insert TableWithIdentity( ... )output inserted.IdentityColumnName into @IdentityOutputvalues( ... )
select @IdentityValue = (select ID from @IdentityOutput)

添加

SELECT CAST(scope_identity() AS int);

到插入sql语句的末尾,然后

NewId = command.ExecuteScalar()

将检索它。

当您使用Entity Framework时,它在内部使用OUTPUT技术返回新插入的ID值

DECLARE @generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)OUTPUT inserted.TurboEncabulatorID INTO @generated_keysVALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID ]FROM @generated_keys AS gJOIN dbo.TurboEncabulators AS tON g.Id = t.TurboEncabulatorIDWHERE @@ROWCOUNT > 0

输出结果存储在临时表变量中,连接回表,并从表中返回行值。

注意:我不知道为什么EF会在临时表内连接回真实表(在什么情况下两者不匹配)。

这就是EF所做的。

此技术(OUTPUT)仅在SQLServer 2008或更高版本上可用。

编辑-加入的原因

Entity Framework连接回原始表而不是简单地使用OUTPUT值的原因是EF也使用这种技术来获取新插入行的rowversion

您可以通过使用#0属性:🕗在实体框架模型中使用乐观并发

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_keysVALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID], t.[RowVersion]FROM @generated_keys AS gJOIN dbo.TurboEncabulators AS tON g.Id = t.TurboEncabulatorIDWHERE @@ROWCOUNT > 0

为了检索这个Timetsamp,你不能使用OUTPUT子句。

这是因为如果表上有一个触发器,你OUTPUT的任何Timestamp都会出错:

  • 初始插入。时间戳:1
  • OUTPUT子句输出时间戳:1
  • 触发器修改行。时间戳:2

如果您在表上有触发器,则返回的时间戳将从未正确。所以您必须使用单独的SELECT

即使您愿意承受不正确的行版本,执行单独的SELECT的另一个原因是您不能将rowversion输出到表变量中:

DECLARE @generated_keys table([Id] uniqueidentifier, [Rowversion] timestamp)
INSERT INTO TurboEncabulators(StatorSlots)OUTPUT inserted.TurboEncabulatorID, inserted.Rowversion INTO @generated_keysVALUES('Malleable logarithmic casing');

这样做的第三个原因是为了对称。当使用触发器在表上执行UPDATE时,您不能使用OUTPUT子句。不支持尝试用OUTPUTUPDATE,并会给出错误:

唯一的方法是使用后续SELECT语句:

UPDATE TurboEncabulatorsSET StatorSlots = 'Lotus-O deltoid type'WHERE ((TurboEncabulatorID = 1) AND (RowVersion = 792))
SELECT RowVersionFROM TurboEncabulatorsWHERE @@ROWCOUNT > 0 AND TurboEncabulatorID = 1

在你的插入语句之后,你需要添加这个。并确保数据插入的表名。

IDENT_CURRENT('tableName')

我无法使用其他版本的SQLServer,但在2012年,直接输出就可以了。

INSERT INTO MyTableOUTPUT INSERTED.IDVALUES (...)

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

INSERT INTO MyTableOUTPUT INSERTED.IDVALUES(...),(...),(...)

产出

ID234

创建一个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';

保证您插入的行的身份的另一种方法是指定身份值并使用SET IDENTITY_INSERT ON然后OFF。这保证您确切地知道身份值是什么!只要值不被使用,那么您就可以将这些值插入到身份列中。

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 INSERTSET 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

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

尽管这是一个较旧的线程,但有一种更新的方法可以避免旧版本的SQLServer比如服务器重启后标识值的缺口中IDENTITY列的一些缺陷。序列在SQLServer 2016中可用,更新的方法是使用TSQL创建SEQUENCE对象。这允许您在SQLServer中创建自己的数字序列对象并控制它的增量方式。

下面是一个例子:

CREATE SEQUENCE CountBy1START WITH 1INCREMENT BY 1 ;GO

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

SELECT NEXT VALUE FOR CountBy1 AS SequenceIDGO

这里是创建序列下一个价值的链接