Deze situatie is niet ongebruikelijk bij het verwerken van bulk INSERT's naar ODBC gekoppelde tabellen in Access. In het geval van de volgende Access-query
INSERT INTO METER_DATA (MPO_REFERENCE)
SELECT MPO_REFERENCE FROM tblTempSmartSSP
waar [METER_DATA] een ODBC-gekoppelde tabel is en [tblTempSmartSSP] een lokale (native) Access-tabel is, is ODBC enigszins beperkt in hoe slim het kan zijn omdat het een breed scala aan doeldatabases moet kunnen accommoderen waarvan de mogelijkheden kunnen variëren enorm. Helaas betekent dit vaak dat, ondanks de enkele Access SQL-instructie, er een aparte INSERT (of equivalent) voor elke rij in de lokale tabel wordt verzonden naar de externe (gekoppelde) database. . Het is begrijpelijk dat dit erg traag kan zijn als de lokale tabel een groot aantal rijen bevat.
Optie 1:Native bulk-inserts naar de externe database
Alle databases hebben een of meer native mechanismen voor het bulksgewijs laden van gegevens:Microsoft SQL Server heeft "bcp" en BULK INSERT
, en Oracle heeft "SQL*Loader". Deze mechanismen zijn geoptimaliseerd voor bulkoperaties en bieden doorgaans aanzienlijke snelheidsvoordelen. Als de gegevens moeten worden geïmporteerd in Access en moeten worden "gemasseerd" voordat ze worden overgebracht naar de externe database, kan het nog steeds sneller zijn om de gewijzigde gegevens terug naar een tekstbestand te dumpen en deze vervolgens in bulk in de externe database te importeren.
Optie 2:Een pass-through-query gebruiken in Access
Als de bulkimportmechanismen geen haalbare optie zijn, is een andere mogelijkheid om een of meer pass-through-query's in Access te bouwen om de gegevens te uploaden met behulp van INSERT-instructies die meer dan één rij tegelijk kunnen invoegen.
Als de externe database bijvoorbeeld SQL Server (2008 of later) was, zouden we een Access Pass-Through (T-SQL)-query zoals deze kunnen uitvoeren
INSERT INTO METER_DATA (MPO_REFERENCE) VALUES (1), (2), (3)
om drie rijen met één INSERT-instructie in te voegen.
Volgens een antwoord op een andere eerdere vraag hier zou de corresponderende syntaxis voor Oracle zijn
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;
Ik heb deze aanpak getest met SQL Server (omdat ik geen toegang heb tot een Oracle-database) met behulp van een native [tblTempSmartSSP]-tabel met 10.000 rijen. De code ...
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
... duurde ongeveer 100 seconden om uit te voeren in mijn testomgeving.
De volgende code daarentegen, die INSERT's met meerdere rijen bouwt zoals hierboven beschreven (met behulp van wat Microsoft een Table Value Constructor noemt) ...
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
... duurde tussen de 1 en 2 seconden om dezelfde resultaten te produceren.
(T-SQL Table Value Constructors zijn beperkt tot het invoegen van 1000 rijen tegelijk, dus de bovenstaande code is iets gecompliceerder dan anders het geval zou zijn.)