与最近的一个相关问题 here 类似,这是另一种情况,将 ODBC 链接表视为本机 Access 表会导致性能下降。
对于名为 [SQLtbl](ODBC 链接到 SQL Server)和 [MDBtbl](本机 Access)的两个相同的 Access 表,每个表有 9999 行,执行以下代码大约需要 5.5 分钟:
Sub UpdateViaJoin()
Dim con As ADODB.Connection
Dim t0 As Single
Set con = CurrentProject.Connection
con.CommandTimeout = 0
t0 = Timer
con.Execute _
"UPDATE " & _
"SQLtbl INNER JOIN MDBtbl " & _
"ON SQLtbl.ID = MDBtbl.ID " & _
"SET SQLtbl.Col1 = MDBtbl.Col1"
Debug.Print Format(Timer - t0, "0.0") & " seconds"
Set con = Nothing
End Sub
为了查看 JOIN 本身是否有问题,我运行了以下命令,只用了 5 分钟多的时间就完成了:
Sub UbdateViaDLookup()
Dim cdb As DAO.Database
Dim t0 As Single
Set cdb = CurrentDb
t0 = Timer
cdb.Execute _
"UPDATE SQLtbl SET Col1 = DLookup(""Col1"", ""MDBtbl"", ""ID="" & ID)"
Debug.Print Format(Timer - t0, "0.0") & " seconds"
Set cdb = Nothing
End Sub
另一方面,以下使用直通查询和原生 T-SQL 准备语句的代码始终在 2 秒内运行(即快 100 倍以上):
Sub UpdateViaPassThroughQuery()
Dim cdb As DAO.Database, rst As DAO.Recordset, qdf As DAO.QueryDef
Dim SQL As String, statementHandle As Long, i As Long, updateList As String
Dim t0 As Single
Set cdb = CurrentDb
t0 = Timer
SQL = "SET NOCOUNT ON;"
SQL = SQL & "DECLARE @statementHandle int;"
SQL = SQL & "EXEC sp_prepare @statementHandle OUTPUT, N'@P1 nvarchar(50), @P2 int', N'UPDATE SQLtbl SET Col1=@P1 WHERE ID=@P2';"
SQL = SQL & "SELECT @statementHandle;"
Set qdf = cdb.CreateQueryDef("")
qdf.Connect = cdb.TableDefs("SQLtbl").Connect
qdf.SQL = SQL
qdf.ReturnsRecords = True
Set rst = qdf.OpenRecordset(dbOpenSnapshot)
statementHandle = rst(0).Value
rst.Close
Set rst = cdb.OpenRecordset("SELECT ID, Col1 FROM MDBtbl", dbOpenSnapshot)
i = 0
updateList = ""
Do Until rst.EOF
i = i + 1
updateList = updateList & "EXEC sp_execute " & statementHandle & ", N'" & Replace(rst!Col1, "'", "''") & "', " & rst!id & ";"
If i = 1000 Then
qdf.SQL = updateList
qdf.ReturnsRecords = False
qdf.Execute
i = 0
updateList = ""
End If
rst.MoveNext
Loop
If i > 0 Then
qdf.SQL = updateList
qdf.ReturnsRecords = False
qdf.Execute
End If
rst.Close
Set rst = Nothing
qdf.SQL = "EXEC sp_unprepare " & statementHandle & ";"
qdf.ReturnsRecords = False
qdf.Execute
Set qdf = Nothing
Debug.Print Format(Timer - t0, "0.0") & " seconds"
Set cdb = Nothing
End Sub
编辑
要调整上述代码以处理 Null,您需要更新该行 ...
updateList = updateList & "EXEC sp_execute " & statementHandle & ", N'" & Replace(rst!Col1, "'", "''") & "', " & rst!id & ";"
...到...
updateList = updateList & "EXEC sp_execute " & statementHandle & ", " & _
FormatArgForPrepStmt(rst!Col1) & ", " & _
rst!id & ";"
...并添加一些类似这样的格式化功能:
Private Function FormatArgForPrepStmt(item As Variant) As String
If IsNull(item) Then
FormatArgForPrepStmt = "NULL"
Else
Select Case VarType(item)
Case vbString
FormatArgForPrepStmt = "N'" & Replace(item, "'", "''") & "'"
Case vbDate
FormatArgForPrepStmt = "N'" & Format(item, "yyyy-mm-dd Hh:Nn:Ss") & "'"
Case Else
FormatArgForPrepStmt = CStr(item)
End Select
End If
End Function