服务器端只进游标中断@@IDENTITY
Server-side forward-only cursor breaks @@IDENTITY
这是一个最小的重现示例。
数据库:
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
预期结果: Debug.Print
行向调试 window.
输出一个整数
实际结果: Debug.Print
行输出 Null
到调试 window.
备注:
- 一旦我删除标记为
[X]
的行,代码就会按预期工作(最后插入的标识值被写入调试 window)。
- 这是一个最小的重现示例:我知道服务器端游标是"evil"。我 知道 在这种特殊情况下,将
SELECT SCOPE_IDENTITY()
添加到 INSERT 批处理是获取新插入 ID 的正确方法。这只是一个用尽可能少的代码重现问题的最小示例。我在修改遗留代码时偶然发现了这个问题。
- 已使用 SQL Server Native Client 11.0 和 "classic" MDAC SQL Server ODBC 驱动程序进行测试。使用 SQL Server 2005 和 2012 进行测试。没有区别。
我的问题:这是设计使然,还是我偶然发现了 SQL 服务器错误?如果是前者,它在哪里记录?
编辑:将两个选项(有和没有 [X])与 SQL Server Profiler 跟踪进行比较,有一个显着差异:当包含 [X] 行时,连接显然已断开并在 cmd.Execute
和 rs2.Open
之间重新开放(Audit Logout
- Audit Login
)。我想这解释了为什么 @@IDENTITY
不再有效。
这留下了以下问题:为什么 ADO(或 SQLNCLI11 驱动程序?)在一种情况下关闭并重新打开连接,而在另一种情况下却没有?这记录在哪里?
我注意到您在同一个连接上进行了两次选择。您是否尝试通过将 "MultipleActiveResultSets=True" 添加到您的连接字符串来启用 "Multiple Active Result Sets"?
MARS 是默认行为的更好替代方法,默认行为实际上允许多个记录集。
发生的事情是:
SELECT 1
充当连接的活动记录集并保持打开状态
- 当您随后执行
insert
时,提供程序知道它有一个活动记录集,并试图通过创建一个新连接来提供帮助来执行该语句而不干扰任何东西
- 此临时连接执行
insert
然后通过执行注销进行清理 - 销毁与其关联的状态
select @@identity
再次使用临时连接,其中前一个语句的 @@identity
超出范围,因此 NULL
.
这是一个最小的重现示例。
数据库:
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
预期结果: Debug.Print
行向调试 window.
实际结果: Debug.Print
行输出 Null
到调试 window.
备注:
- 一旦我删除标记为
[X]
的行,代码就会按预期工作(最后插入的标识值被写入调试 window)。 - 这是一个最小的重现示例:我知道服务器端游标是"evil"。我 知道 在这种特殊情况下,将
SELECT SCOPE_IDENTITY()
添加到 INSERT 批处理是获取新插入 ID 的正确方法。这只是一个用尽可能少的代码重现问题的最小示例。我在修改遗留代码时偶然发现了这个问题。 - 已使用 SQL Server Native Client 11.0 和 "classic" MDAC SQL Server ODBC 驱动程序进行测试。使用 SQL Server 2005 和 2012 进行测试。没有区别。
我的问题:这是设计使然,还是我偶然发现了 SQL 服务器错误?如果是前者,它在哪里记录?
编辑:将两个选项(有和没有 [X])与 SQL Server Profiler 跟踪进行比较,有一个显着差异:当包含 [X] 行时,连接显然已断开并在 cmd.Execute
和 rs2.Open
之间重新开放(Audit Logout
- Audit Login
)。我想这解释了为什么 @@IDENTITY
不再有效。
这留下了以下问题:为什么 ADO(或 SQLNCLI11 驱动程序?)在一种情况下关闭并重新打开连接,而在另一种情况下却没有?这记录在哪里?
我注意到您在同一个连接上进行了两次选择。您是否尝试通过将 "MultipleActiveResultSets=True" 添加到您的连接字符串来启用 "Multiple Active Result Sets"?
MARS 是默认行为的更好替代方法,默认行为实际上允许多个记录集。
发生的事情是:
SELECT 1
充当连接的活动记录集并保持打开状态- 当您随后执行
insert
时,提供程序知道它有一个活动记录集,并试图通过创建一个新连接来提供帮助来执行该语句而不干扰任何东西 - 此临时连接执行
insert
然后通过执行注销进行清理 - 销毁与其关联的状态 select @@identity
再次使用临时连接,其中前一个语句的@@identity
超出范围,因此NULL
.