服务器端仅向前游标致@@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
相关产品推荐
相关产品推荐

