Server-side forward-only cursor breaks @@IDENTITY

醉酒当歌 提交于 2019-12-23 18:13:32

问题


Here is a minimal repro example.

Database:

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

Code (using VBA and 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

Expected result: The Debug.Print line outputs an integer to the debug window.

Actual result: The Debug.Print line outputs Null to the debug window.

Notes:

  • As soon as I remove the lines marked as [X], the code works as expected (the last inserted identity value is written to the debug window).
  • This is a minimal repro example: I know that server-side cursors are "evil". I know that in this particular case adding SELECT SCOPE_IDENTITY() to the INSERT batch is the correct way to get the newly inserted ID. This is just a minimal example to reproduce the issue with the least code possible. I stumbled upon this issue while modifying legacy code.
  • Tested with both SQL Server Native Client 11.0 and the "classic" MDAC SQL Server ODBC driver. Tested with SQL Server 2005 and 2012. Doesn't make a difference.

My question: Is this behavior by design, or did I stumble upon an SQL Server bug? If the former, where is it documented?


EDIT: Comparing the two options (with and without [X]) with an SQL Server Profiler trace, there is one significant difference: When the [X] lines are included, the connection is apparently dropped and reopened (Audit Logout - Audit Login) between cmd.Execute and rs2.Open. I guess that explains why @@IDENTITY no longer works.

That leaves the following question: Why does ADO (or the SQLNCLI11 driver?) close and reopen the connection in one case but not in the other case? And where is this documented?


回答1:


MARS is a nicer alternative to the default behavior which does actually allow multiple recordsets.

What happens is:

  1. SELECT 1 acts as the active recordset for the connection & remains open
  2. When you then execute the insert the provider knows it has an active recordset and tries to be helpful by creating a new connection to execute the statement without interfering with anything
  3. This ephemeral connection executes the insert then tidies up by perfoming a logout - destroying state associated with it
  4. select @@identity again uses an ephemeral connection where @@identity for the previous statement is out of scope, hence NULL.




回答2:


I noticed you are doing two selects on the same connection. Have you tried enabling "Multiple Active Result Sets" by adding "MultipleActiveResultSets=True" to your connection string?

Enabling Multiple Active Result Sets on MSDN



来源:https://stackoverflow.com/questions/48499514/server-side-forward-only-cursor-breaks-identity

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!