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

Ode*_*ded 1056 sql t-sql sql-server

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

我知道@@IDENTITYIDENT_CURRENTSCOPE_IDENTITY,但不明白连接到每个利弊.

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

bdu*_*kes 1363

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

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

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

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

  • 为了防止其他人受到惊吓,上面提到的错误在SQL Server 2008 R2 Service Pack 1的累积更新5中得到了修复. (82认同)
  • SCOPE_IDENTITY()的已知错误返回错误的值:http://blog.sqlauthority.com/2009/03/24/sql-server-2008-scope_identity-bug-with-multi-processor-parallel-plan-and-solution /解决方法是不在多处理器并行计划中运行INSERT或使用OUTPUT子句 (45认同)
  • 几乎每次我想要'身份',我都想知道我刚刚插入的记录的密钥.如果这是您的情况,您想要使用OUTPUT子句.如果你想要别的东西,请努力阅读并理解bdukes的反应. (2认同)
  • 使用`output`,您无需创建临时表来存储和查询结果.只需省略输出子句的`into`部分,它就会将它们输出到结果集中. (2认同)

Orr*_*rry 171

我相信检索插入的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
Run Code Online (Sandbox Code Playgroud)

  • 有关获取插入ID的简洁示例,请查看:http://stackoverflow.com/a/10999467/2003325 (8认同)
  • 哇噢!OUTPUT CLAUSE rocks :)这将简化我当前的任务.以前不知道那句话.感谢你们! (6认同)
  • 是的,这是正确的方法,如果你不在SQL Server 2008上,只使用其中一个(我们跳过了2005,所以不确定OUTPUT是否可用) (3认同)

Ori*_*rds 104

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

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

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

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

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

  • @SlavaCa 它为每个连接的每个 SQL 语句返回正确的记录。如果您有 5 个用户同时创建记录,则可能有 5 个不同的数据库连接,因此每个用户都会获得自己的身份。有用 :-) (3认同)

Ian*_*emp 63

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

  • SQL Server群集是一种高可用性功能,与并行性无关.单行插入(`scope_identity()`的最常见情况)无论如何都要获得并行计划是非常罕见的.这个错误在这个答案之前一年多修复了. (5认同)
  • 谢谢,这是我能找到的唯一一个例子,它展示了如何在变量中使用输出中的值,而不仅仅是输出它。 (2认同)

小智 23

SELECT CAST(scope_identity() AS int);
Run Code Online (Sandbox Code Playgroud)

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

NewId = command.ExecuteScalar()
Run Code Online (Sandbox Code Playgroud)

将检索它.


Ian*_*oyd 15

使用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
Run Code Online (Sandbox Code Playgroud)

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

注意:我不知道为什么EF会将短暂的表连接回真实表(在什么情况下两者不匹配).

但这就是EF所做的.

此技术(OUTPUT)仅适用于SQL Server 2008或更高版本.

  • 我想象它们与它们匹配以确保完整性(例如,在乐观并发模式下,当您从表变量中选择时,可能有人删除了插入程序行)。另外,也喜欢您的`TurboEncabulators` :) (2认同)

Jak*_*urc 14

MSDN

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

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

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


Guy*_*uck 13

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

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

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

  • 您不应该为此目的使用@@ identity.如果有人稍后添加了触发器,您将失去数据完整性.@@ identiy是一种极其危险的做法. (2认同)

Mar*_*ese 12

我无法与其他版本的SQL Server通信,但在2012年,直接输出工作正常.您不需要打扰临时表.

INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES (...)
Run Code Online (Sandbox Code Playgroud)

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

INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES
    (...),
    (...),
    (...)
Run Code Online (Sandbox Code Playgroud)

产量

ID
2
3
4
Run Code Online (Sandbox Code Playgroud)


eri*_*len 8

总是使用scope_identity(),不需要任何其他东西.

  • 不完全*从不*但是100次中的99次,你将使用Scope_Identity(). (13认同)
  • 如果使用INSERT-SELECT插入多行,则需要使用OUTPUT子句捕获多个ID (10认同)
  • 查看Orry(http://stackoverflow.com/a/6073578/2440976)这个问题的答案 - 并行,并且作为一种最佳实践,你应该明智地遵循他的设置......真是太棒了! (2认同)