为啥 SCOPE_IDENTITY() 不返回我插入的 ID?

Posted

技术标签:

【中文标题】为啥 SCOPE_IDENTITY() 不返回我插入的 ID?【英文标题】:Why isn't SCOPE_IDENTITY() returning my inserted ID?为什么 SCOPE_IDENTITY() 不返回我插入的 ID? 【发布时间】:2017-02-22 14:11:13 【问题描述】:

我有一个插入语句通过ADODB.Connection.Execute 插入到具有标识列的 MSSQL 服务器上的表中,但返回的记录集不返回新插入记录的 ID。

我尝试了以下几种方式来获取记录的插入ID。

INSERT INTO blah VALUES (blah); SELECT SCOPE_IDENTITY()
INSERT INTO blah VALUES (blah); SELECT @@IDENTITY
INSERT INTO blah OUTPUT INSERTED.ID INTO @ID VALUES(blah); SELECT ID FROM @ID

在所有情况下,当查询返回的记录集时,它不包含标识

var I = DB.Execute(insert_statement_from_above);
if (I) 
  var INSERTED_ID = I(0).Value;

为什么会发生这种情况?

【问题讨论】:

“自动编号栏”,你是说身份栏吗? 感谢您发现术语错误 【参考方案1】:

检查要插入的表是否有任何 INSERT 触发器,并检查这些 INSERT 触发器是否使用 SET NOCOUNT ON。

当触发器没有指定 SET NOCOUNT ON 时,如果它更新数据库,它会生成一个结果集,该结果集将出现在包含 SCOPE_IDENTITY 结果的结果集之前。

见:https://msdn.microsoft.com/en-us/library/ms189837.aspx

实际上发生的是记录集包含一个或多个结果集。

TRIGGER RESULT SET
SCOPE_IDENTITY RESULT SET

当您查询记录集以获取范围标识时,您实际上是在查询触发器输出的结果集。

您可以使用RS.nextRecordset(),然后查询SCOPE_IDENTITY,但如果触发器可能会或可能不会执行更新,您最终不知道哪个结果集包含您的SCOPE_IDENTITY,是第一个还是第二个,是还有一秒钟吗?

对于触发器来说,指定SET NOCOUNT ON 是公认的良好做法,但是如果您的触发器中绝对必须有SET NOCOUNT OFF 或者无法控制触发器,您可以使用以下代码解决它。

注意:以上示例中的范围标识将始终是记录集中的最后一个结果集。所以我们可以找到如下:

var I = DB.Execute(insert_statement_from_above);
if (I) 
  // find the result set with the scope identity in it
  while (I.State == 0) 
    I = I.nextRecordset();
  
  var INSERTED_ID = I(0).Value;

这会通过忽略先前关闭的结果集来查找包含您的作用域标识的结果集。

另见:

State property, nextRecordset()

【讨论】:

【参考方案2】:

如果您可以阻止所有触发器返回结果集,则服务器选项“禁止从触发器返回结果”可能是您的解决方案。

sp_configure 'disallow results from triggers', 1
GO
RECONFIGURE
GO

【讨论】:

以上是关于为啥 SCOPE_IDENTITY() 不返回我插入的 ID?的主要内容,如果未能解决你的问题,请参考以下文章

php 使用 Scope_identity 返回 id

就是我插着耳机,手机在放一首歌,录视频为啥不能将歌旳声音录进去

select SCOPE_IDENTITY()用法

@@ROWCOUNT,@@IDENTITY, SCOPE_IDENTITY()

OUTPUT INSERTED Id/SCOPE_IDENTITY() 在 C# (ASP.NET Core Razor Pages) SQL 查询中返回 null,在 SQL Server Mana

SCOPE_IDENTITY()和 SELECT @@IDENTITY 的用法