Denne situation er ikke ualmindeligt, når man håndterer bulk INSERTs til ODBC-linkede tabeller i Access. I tilfælde af følgende Access-forespørgsel
INSERT INTO METER_DATA (MPO_REFERENCE)
SELECT MPO_REFERENCE FROM tblTempSmartSSP
hvor [METER_DATA] er en ODBC-linket tabel og [tblTempSmartSSP] er en lokal (native) Access-tabel, er ODBC noget begrænset i, hvor smart den kan være, fordi den skal være i stand til at rumme en bred vifte af måldatabaser, hvis muligheder kan variere i høj grad. Desværre betyder det ofte, at på trods af den enkelte Access SQL-sætning, er det, der faktisk sendes til den eksterne (linkede) database, en separat INSERT (eller tilsvarende) for hver række i den lokale tabel . Det kan forståeligt nok vise sig at være meget langsomt, hvis den lokale tabel indeholder et stort antal rækker.
Mulighed 1:Indbyggede masseindsættelser til fjerndatabasen
Alle databaser har en eller flere indbyggede mekanismer til masseindlæsning af data:Microsoft SQL Server har "bcp" og BULK INSERT
, og Oracle har "SQL*Loader". Disse mekanismer er optimeret til bulkoperationer og vil normalt give betydelige hastighedsfordele. Faktisk, hvis dataene skal importeres til Access og "masseres", før de overføres til fjerndatabasen, kan det stadig være hurtigere at dumpe de ændrede data tilbage til en tekstfil og derefter masseimportere dem til fjerndatabasen.
Mulighed 2:Brug af en pass-through-forespørgsel i Access
Hvis masseimportmekanismerne ikke er en mulig mulighed, så er en anden mulighed at bygge en eller flere pass-through-forespørgsler i Access for at uploade dataene ved hjælp af INSERT-sætninger, der kan indsætte mere end én række ad gangen.
For eksempel, hvis fjerndatabasen var SQL Server (2008 eller nyere), så kunne vi køre en Access pass-through (T-SQL) forespørgsel som denne
INSERT INTO METER_DATA (MPO_REFERENCE) VALUES (1), (2), (3)
for at indsætte tre rækker med én INSERT-sætning.
Ifølge et svar på et andet tidligere spørgsmål her ville den tilsvarende syntaks for Oracle være
INSERT ALL
INTO METER_DATA (MPO_REFERENCE) VALUES (1)
INTO METER_DATA (MPO_REFERENCE) VALUES (2)
INTO METER_DATA (MPO_REFERENCE) VALUES (3)
SELECT * FROM DUAL;
Jeg testede denne tilgang med SQL Server (da jeg ikke har adgang til en Oracle-database) ved hjælp af en indbygget [tblTempSmartSSP]-tabel med 10.000 rækker. Koden ...
Sub LinkedTableTest()
Dim cdb As DAO.Database
Dim t0 As Single
t0 = Timer
Set cdb = CurrentDb
cdb.Execute _
"INSERT INTO METER_DATA (MPO_REFERENCE) " & _
"SELECT MPO_REFERENCE FROM tblTempSmartSSP", _
dbFailOnError
Set cdb = Nothing
Debug.Print "Elapsed time " & Format(Timer - t0, "0.0") & " seconds."
End Sub
... tog cirka 100 sekunder at udføre i mit testmiljø.
Derimod følgende kode, som bygger INSERT'er med flere rækker som beskrevet ovenfor (ved at bruge det, Microsoft kalder en Table Value Constructor) ...
Sub PtqTest()
Dim cdb As DAO.Database, rst As DAO.Recordset
Dim t0 As Single, i As Long, valueList As String, separator As String
t0 = Timer
Set cdb = CurrentDb
Set rst = cdb.OpenRecordset("SELECT MPO_REFERENCE FROM tblTempSmartSSP", dbOpenSnapshot)
i = 0
valueList = ""
separator = ""
Do Until rst.EOF
i = i + 1
valueList = valueList & separator & "(" & rst!MPO_REFERENCE & ")"
If i = 1 Then
separator = ","
End If
If i = 1000 Then
SendInsert valueList
i = 0
valueList = ""
separator = ""
End If
rst.MoveNext
Loop
If i > 0 Then
SendInsert valueList
End If
rst.Close
Set rst = Nothing
Set cdb = Nothing
Debug.Print "Elapsed time " & Format(Timer - t0, "0.0") & " seconds."
End Sub
Sub SendInsert(valueList As String)
Dim cdb As DAO.Database, qdf As DAO.QueryDef
Set cdb = CurrentDb
Set qdf = cdb.CreateQueryDef("")
qdf.Connect = cdb.TableDefs("METER_DATA").Connect
qdf.ReturnsRecords = False
qdf.sql = "INSERT INTO METER_DATA (MPO_REFERENCE) VALUES " & valueList
qdf.Execute dbFailOnError
Set qdf = Nothing
Set cdb = Nothing
End Sub
... tog mellem 1 og 2 sekunder at producere de samme resultater.
(T-SQL Table Value Constructors er begrænset til at indsætte 1000 rækker ad gangen, så ovenstående kode er en smule mere kompliceret, end den ellers ville være.)