复制
创建一个包含很多行的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;
创建一个新的VBA项目(例如Access或Excel 2016),并添加对“ Microsoft ActiveX数据对象2.8(或6.1)库”的引用。
修改以下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
之后的CursorLocation
和rs2.Open
会发现,在两种情况下(慢速和快速),都返回了客户端静态游标。
我已经测试了以下SQL Server ODBC驱动程序,并且所有这些问题都可以重现该问题:
{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。我们刚刚完成了从它们的迁移。