为什么打开第二个ADO客户端记录集的速度如此之慢?

时间:2018-07-30 13:45:32

标签: sql-server vba ms-access ado

复制

  1. 创建一个包含很多行的SQL Server表:

    CREATE TABLE largetable (field int);
    
    INSERT INTO largetable (field)
    SELECT TOP 10000 ROW_NUMBER() OVER(ORDER BY t1.number) 
      FROM master..spt_values t1 CROSS JOIN master..spt_values;
    
  2. 创建一个新的VBA项目(例如Access或Excel 2016),并添加对“ Microsoft ActiveX数据对象2.8(或6.1)库”的引用。

  3. 修改以下repro代码以包含与SQL Server数据库的正确连接字符串。然后在您的VBA模块中执行它:

    Public Sub Repro()
        Dim cn As New ADODB.Connection
        Dim r1 As New ADODB.Recordset
    
        cn.ConnectionString = "Driver={SQL Server Native Client 11.0};Server=...;Database=...;Trusted_Connection=yes"
        cn.Open
    
        ReadLargeTable cn       ' Fast (0.01-0.03s)
    
        r1.CursorLocation = adUseClient
        r1.Open "SELECT 1", cn, adOpenStatic
    
        ReadLargeTable cn       ' Slow (6-10s)
    
        r1.Close
    
        ReadLargeTable cn       ' Slow (6-10s)
    
        Set r1 = Nothing
    
        ReadLargeTable cn       ' Fast (0.01-0.03s)
    
        cn.Close
    End Sub
    
    Private Sub ReadLargeTable(ByVal cn As ADODB.Connection)
        Dim d As Double
        Dim r2 As New ADODB.Recordset
    
        d = Timer
        r2.CursorLocation = adUseClient
        r2.Open "SELECT field FROM largetable", cn, adOpenStatic
        Debug.Print Timer - d
    
        r2.Close
        Set r2 = Nothing
    End Sub
    

问题

如您所见,如果已经打开另一个客户端游标,则打开第二个客户端游标会非常痛苦。我想知道为什么会这样我能做些什么


更多详细信息

使用SQL Server Profiler,我可以看到“慢速”和“快速”场景有所不同。

这是“快速”查询的样子:

SQL:BatchStarting   SELECT field FROM largetable
SQL:StmtStarting    SELECT field FROM largetable
SQL:StmtCompleted   SELECT field FROM largetable
SQL:BatchCompleted  SELECT field FROM largetable

这是一个“慢”查询的样子:

RPC:Starting
  declare @p1 int
  set @p1=0
  declare @p3 int
  set @p3=16388
  declare @p4 int
  set @p4=8193
  declare @p5 int
  set @p5=0
  exec sp_cursoropen @p1 output,N'SELECT field FROM largetable',@p3 output,@p4 output,@p5 output
  select @p1, @p3, @p4, @p5

RPC:Completed
  ...same SQL as above...

RPC:Starting    exec sp_cursorfetch 180150003,2,0,1
RPC:Completed   exec sp_cursorfetch 180150003,2,0,1
RPC:Starting    exec sp_cursorfetch 180150003,2,0,1
RPC:Completed   exec sp_cursorfetch 180150003,2,0,1
RPC:Starting    exec sp_cursorfetch 180150003,2,0,1
RPC:Completed   exec sp_cursorfetch 180150003,2,0,1
...repeat 10000 times...

因此看来,当查询速度快时,所有数据都被批量加载,而当查询速度慢时,每条记录都被单独传输。

很明显,即使另一个客户端游标已经打开,我也想强迫ADO始终使用“快速”路由。


附加说明

  • 我知道Recordset.Open可以返回与所请求的游标不同类型的游标。在这种情况下,检查CursorType之后的CursorLocationrs2.Open会发现,在两种情况下(慢速和快速),都返回了客户端静态游标。

  • 我已经测试了以下SQL Server ODBC驱动程序,并且所有这些问题都可以重现该问题:

  • 使用SQL Server OLE DB驱动程序不能不能重现该问题。我们使用ODBC而不是OLE DB,因为OLE DB driver was deprecated。我知道它在一段时间之前不推荐使用,但是我们目前不打算迁移DAL。

  • 启用MARS(MARS_Connection=yes)并没有什么不同。 SQL Server Profiler显示两个记录集使用相同的连接。这是not that problem

  • 我们使用ADO而不是ADO.NET,因为MS Access还没有对.NET代码的内置支持。

  • 我们不想使用服务器端游标。他们are evil and have their own sets of problems。我们刚刚完成了从它们的迁移。

0 个答案:

没有答案