You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

服务器端仅向前游标致@@IDENTITY失效:是设计还是Bug?

ADO+SQL Server 连接重连导致@@IDENTITY失效的原因解析

这是一个典型的ADO驱动与SQL Server交互的设计行为问题,不是Bug,我来帮你梳理清楚来龙去脉:

最小复现场景

数据库准备

CREATE TABLE temp (x int IDENTITY(1, 1), y int);

VBA+ADO代码

Public Sub repro()
    Dim cn As New Connection
    Dim rs1 As New Recordset
    Dim cmd As New Command
    Dim rs2 As New Recordset
    cn.Open "Provider=SQLNCLI11;Server=myServer;Database=myDatabase;Trusted_Connection=Yes"
    rs1.Open "SELECT 1", cn, adOpenForwardOnly ' [X] 标记行
    cmd.ActiveConnection = cn
    cmd.CommandText = "INSERT INTO temp (y) VALUES (1) "
    cmd.Execute
    rs2.Open "SELECT @@IDENTITY", cn, adOpenStatic
    Debug.Print rs2(0).value
    rs2.Close
    rs1.Close ' [X] 标记行
    cn.Close
End Sub

现象对比

  • 预期结果:调试窗口输出插入的标识整数
  • 实际结果:输出Null
  • 修复操作:移除或提前执行两个标记为[X]的行后,结果恢复正常

关键现象追踪(SQL Server Profiler)

你通过Profiler发现的Audit Logout - Audit Login序列是核心线索:当保留rs1.Open且未提前关闭时,在cmd.Execute和rs2.Open之间,驱动会断开原有连接并重新建立新连接。这直接导致@@IDENTITY失效——因为@@IDENTITY是会话级变量,仅在当前连接会话中有效,新连接无法获取之前会话的插入标识。

原因:ADO驱动的设计行为

这是SQL Server Native Client(以及旧版MDAC ODBC驱动)的既定设计,根源在于服务器端游标的会话占用特性:

  • 当你使用adOpenForwardOnly打开Recordset时,默认情况下驱动会采用服务器端游标(除非显式指定客户端游标)。服务器端游标是绑定到当前连接会话的,在游标未关闭前,该连接的会话会被游标独占,无法并行执行其他需要会话上下文的命令。
  • 当后续执行rs2.Open时,驱动发现原有连接被服务器端游标占用,会自动从连接池中获取新连接(或重新建立连接)来处理这个查询,这就导致SELECT @@IDENTITY运行在全新的会话中,自然无法获取之前插入操作的标识值。
  • 而移除rs1.Open或提前关闭rs1后,连接会话不再被游标占用,所有操作都在同一个会话内完成,@@IDENTITY就能正确返回结果。

相关文档参考

微软官方文档中关于SQL Server驱动与ADO游标交互的部分明确提到了这一点:

  • 服务器端游标会占用连接会话,在游标关闭前,同一连接无法执行其他命令,驱动会自动创建新连接来处理后续请求。
  • @@IDENTITY、SCOPE_IDENTITY()等函数的会话级特性,也在SQL Server的官方文档中有明确说明。

额外建议

正如你已经了解的,在这类场景下,最可靠的做法是将插入和标识查询放在同一批处理中,比如:

INSERT INTO temp (y) VALUES (1);
SELECT SCOPE_IDENTITY() AS NewID;

这样无论连接是否切换,都能直接获取当前插入操作的标识值,避免依赖会话状态。


内容的提问来源于stack exchange,提问作者Heinzi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:56:27