Hei*_*nzi 5 sql-server ms-access vba ado
再现
创建一个包含很多行的 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;
Run Code Online (Sandbox Code Playgroud)创建一个新的 VBA 项目(例如 Access 或 Excel 2016)并添加对“Microsoft ActiveX 数据对象 2.8(或 6.1)库”的引用。
修改以下重现代码以包含 SQL Server 数据库的正确连接字符串。然后在 VBA 模块中执行它:
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;
Run Code Online (Sandbox Code Playgroud)问题
正如您所看到的,如果已经打开了另一个客户端游标,则打开第二个客户端游标的速度会非常慢。我想知道为什么会发生这种情况以及我可以采取什么措施。
更多细节
使用 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
Run Code Online (Sandbox Code Playgroud)
这是“慢”查询的样子:
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...
Run Code Online (Sandbox Code Playgroud)
因此,当查询速度快时,所有数据都会批量加载,而当查询速度慢时,每条记录都会单独传输。
显然,我想强制 ADO 始终使用“快速”路径,即使另一个客户端游标已打开。
补充笔记
我知道Recordset.Open可以返回与请求的游标类型不同的游标类型。在这种情况下,检查CursorType和CursorLocation之后rs2.Open显示在两种情况(慢速和快速)中都返回了客户端静态游标。
我已经测试了以下 SQL Server ODBC 驱动程序,并且所有这些驱动程序都可以重现该问题:
{SQL Server}ODBC 驱动程序,使用 SQL Server OLE DB 驱动程序无法重现该问题。我们使用 ODBC 而不是 OLE DB,因为OLE DB 驱动程序已被弃用。我知道它不久前尚未弃用,但我们目前不打算迁移我们的 DAL。
启用 MARS ( MARS_Connection=yes) 不会产生任何影响。SQL Server Profiler 显示两个记录集使用相同的连接。这不是那个问题。
我们使用 ADO 而不是 ADO.NET,因为 MS Access 还没有对 .NET 代码的内置支持。
我们不想使用服务器端游标。他们是邪恶的,并且有自己的一系列问题。我们刚刚完成远离他们的迁移。
如果记录集指定adUseClient但连接没有指定,则第二个 adUseClient记录集会使一切变得非常慢。
当用户同时打开两个表单(每个表单都有一个 ADO RecordSource)时,我就会看到这一点。只需打开一张表格,一切都很好。打开两个表格,算了!
解决方案是建立第二个连接,即adUseClient. 任意数量的记录集都可以使用此连接,而不会降低速度。表单Form_Load通常包含Set Me.Recordset = BackendQueryUseClient("SELECT * FROM MyTable").
我正在使用 SQL Server 身份验证。我克隆当前连接并将第二个连接存储在首次使用时创建的静态中,因此它可以用于后续请求,而无需为每个请求打开新连接的开销。如果需要,我使用该DiscardCache选项来关闭连接。
Public Function BackendQueryUseClient(ByVal q As String) As adodb.Recordset
Dim rs As adodb.Recordset
Set rs = BackendConnectionUseClient.Execute(q)
Set BackendQueryUseClient = rs
End Function
Public Function BackendConnectionUseClient() As adodb.Connection
Static pConnection As adodb.Connection
If pConnection Is Nothing Then
Dim cs As String, csNew As String
cs = CurrentDb.TableDefs("Employees").Connect ' choose any table that will be in the Backend DB
Set pConnection = New adodb.Connection ' existing connection string won't round-trip so must be rearranged
pConnection.CursorLocation = adUseClient
csNew = "ODBC;DATABASE=" & CSValue("DATABASE", cs) & _
";SERVER=" & CSValue("SERVER", cs) & _
";DRIVER=ODBC Driver 17 for SQL Server;" & _
";UID=" & CSValue("UID", cs) & _
";PWD=" & CSValue("PWD", cs)
pConnection.Open csNew
pConnection.Execute "USE [" & CSValue("DATABASE", cs) & "]"
End If
Set BackendConnectionUseClient = pConnection
End Function
Public Function CSValue(Keyword As String, ConnectionString As String) As String
CSValue = RegExpFirstMatch(Keyword & "=([^;]+)", ConnectionString)
End Function
' Return "" if no match found
Public Function RegExpFirstMatch(Pattern As String, SourceString As String) As String
Dim RegEx As RegExp, matches As MatchCollection
Set RegEx = New RegExp
RegEx.Pattern = Pattern
Set matches = RegEx.Execute(SourceString)
If matches.Count > 0 Then
RegExpFirstMatch = matches.Item(0).SubMatches(0)
End If
End Function
Run Code Online (Sandbox Code Playgroud)