【问题标题】:Server-side forward-only cursor breaks @@IDENTITY服务器端只进游标中断@@IDENTITY
【发布时间】:2018-01-29 10:53:17
【问题描述】:

这是一个最小的复制示例。

数据库:

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 行向调试窗口输出一个整数。

实际结果:Debug.Print 行将Null 输出到调试窗口。

注意事项:

  • 一旦我删除标记为[X] 的行,代码就会按预期工作(最后插入的标识值被写入调试窗口)。
  • 这是一个最小的重现示例:我知道服务器端游标是“邪恶的”。我知道在这种特殊情况下,将 SELECT SCOPE_IDENTITY() 添加到 INSERT 批处理是获取新插入的 ID 的正确方法。这只是用尽可能少的代码重现问题的最小示例。我在修改遗留代码时偶然发现了这个问题。
  • 使用 SQL Server Native Client 11.0 和“经典”MDAC SQL Server ODBC 驱动程序进行测试。使用 SQL Server 2005 和 2012 进行测试。没有区别。

我的问题:这种行为是设计使然,还是我偶然发现了 SQL Server 错误?如果是前者,它记录在哪里?


编辑:将两个选项(带和不带 [X])与 SQL Server Profiler 跟踪进行比较,有一个显着的区别:当包含 [X] 行时,连接显然被删除并重新打开(Audit Logout - Audit Login) 在cmd.Executers2.Open 之间。我想这解释了为什么 @@IDENTITY 不再有效。

这留下了以下问题:为什么 ADO(或 SQLNCLI11 驱动程序?)在一种情况下关闭并重新打开连接,而在另一种情况下却没有?这是在哪里记录的?

【问题讨论】:

  • 可能两者都不是。光标需要一堆不可见的设置(如果启用分析,您可以看到其中的一部分)。作为该隐形设置的一部分,我可以生动地想象@@IDENTITY 会因为第一个行集游标仍然处于活动状态并且需要在幕后进行一些操作而感到无聊。尝试连接分析器,看看是否有任何类似性质的东西通过网络发送。
  • @JeroenMostert:好主意。我做了一个分析器跟踪并将结果添加到我的问题中。
  • ADO 关闭和打开连接还首先解释了此代码如何工作——因为没有 MARS,连接上只能有一个活动结果集。您的代码,如果它按字面意思工作,则需要两个。如果 rs2 已打开,则从 rs1 读取将/应该失败,并且在尝试打开 rs2 之前关闭 rs1 应该与消除 rs1 具有相同的效果。尝试检查rs1.State;我希望它会因rs2 打开而隐式关闭。这可能是 ADO 故障保护,因为糟糕的清理代码很常见;它弄乱了你的身份是无意的。
  • 请注意,如果这 你的问题,SCOPE_IDENTITY() 也不会解决任何问题——唯一会为rs2 显式创建一个新连接所以有没有来自另一个可能在cn 上打开的记录集的干扰。 (什么也可能有效,但我不确定,在命令中合并一个输出参数并使用类似INSERT ... ; SELECT @outparm = SCOPE_IDENTITY() 的东西。这避免了行集,但在行集处于活动状态时确实会发回一些东西,而且我'我不确定这是否合法。)
  • @JeroenMostert:有道理,感谢您的分析。有趣的是,rs1.State(官方)仍然是adStateOpen,直到rs1.Close。关于您的最后一条评论:使用命令 INSERT ...; SELECT SCOPE_IDENTITY() 打开记录集可以正常工作(但需要 rs2.NextRecordset)。

标签: sql-server vba oledb ado identity


【解决方案1】:

MARS 是一个更好的替代默认行为,它实际上允许多个记录集。

会发生什么:

  1. SELECT 1 充当连接的活动记录集并保持打开状态
  2. 当您随后执行 insert 时,提供者知道它有一个活动记录集,并尝试通过创建新连接来执行该语句而不干扰任何事情来提供帮助
  3. 此临时连接会执行 insert,然后通过执行注销 - 破坏与其关联的状态来整理
  4. select @@identity 再次使用临时连接,其中前一条语句的 @@identity 超出范围,因此 NULL

【讨论】:

  • 哇,这比我猜的要阴险得多——甚至不能保证它会使用你传递的同一个连接对象,它会很高兴地创建一个新的对象而不告诉你。一方面,我为不复制而不是猜测而感到难过。另一方面,我觉得不再有现成的设置来重现与 ADO 相关的问题......
  • 不错的收获!我确实对其进行了概要分析,但我从未想过 ADO.Connection 会默默打开新连接的想法,所以我没有注意 SPID 列...
【解决方案2】:

我注意到您在同一连接上进行了两次选择。您是否尝试通过将“MultipleActiveResultSets=True”添加到连接字符串来启用“多个活动结果集”?

Enabling Multiple Active Result Sets on MSDN

【讨论】:

  • 这是 ADO,不是 ADO.NET。我还是试过了:MultipleActiveResultSets=True 没有区别。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-15
  • 2017-05-17
  • 2015-08-11
  • 2015-03-24
  • 1970-01-01
  • 1970-01-01
  • 2019-03-03
相关资源
最近更新 更多