Diese Situation ist nicht ungewöhnlich, wenn es um Massen-INSERTs in mit ODBC verknüpfte Tabellen in Access geht. Im Falle der folgenden Access-Abfrage
INSERT INTO METER_DATA (MPO_REFERENCE)
SELECT MPO_REFERENCE FROM tblTempSmartSSP
wobei [METER_DATA] eine mit ODBC verknüpfte Tabelle und [tblTempSmartSSP] eine lokale (native) Access-Tabelle ist, ist ODBC in seiner Cleverness etwas eingeschränkt, da es in der Lage sein muss, eine breite Palette von Zieldatenbanken aufzunehmen, deren Fähigkeiten variieren können sehr. Leider bedeutet dies oft, dass trotz der einzelnen Access-SQL-Anweisung tatsächlich ein separates INSERT (oder Äquivalent) für jede Zeile in der lokalen Tabelle an die entfernte (verknüpfte) Datenbank gesendet wird . Verständlicherweise kann sich das als sehr langsam erweisen, wenn die lokale Tabelle eine große Anzahl von Zeilen enthält.
Option 1:Native Masseneinfügungen in die Remote-Datenbank
Alle Datenbanken haben einen oder mehrere native Mechanismen für das Massenladen von Daten:Microsoft SQL Server hat "bcp" und BULK INSERT
, und Oracle hat "SQL*Loader". Diese Mechanismen sind für Massenoperationen optimiert und bieten normalerweise erhebliche Geschwindigkeitsvorteile. Wenn die Daten in Access importiert und "massiert" werden müssen, bevor sie in die entfernte Datenbank übertragen werden, kann es sogar noch schneller sein, die geänderten Daten wieder in eine Textdatei zu kopieren und sie dann massenhaft in die entfernte Datenbank zu importieren.
Option 2:Verwenden einer Pass-Through-Abfrage in Access
Wenn die Massenimportmechanismen keine praktikable Option sind, besteht eine andere Möglichkeit darin, eine oder mehrere Pass-Through-Abfragen in Access zu erstellen, um die Daten mithilfe von INSERT-Anweisungen hochzuladen, die mehr als eine Zeile gleichzeitig einfügen können.
Wenn die entfernte Datenbank beispielsweise SQL Server (2008 oder höher) war, könnten wir eine Access Pass-Through (T-SQL)-Abfrage wie diese ausführen
INSERT INTO METER_DATA (MPO_REFERENCE) VALUES (1), (2), (3)
um drei Zeilen mit einer INSERT-Anweisung einzufügen.
Laut einer Antwort auf eine andere frühere Frage hier wäre die entsprechende Syntax für Oracle
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;
Ich habe diesen Ansatz mit SQL Server getestet (da ich keinen Zugriff auf eine Oracle-Datenbank habe) und eine native [tblTempSmartSSP]-Tabelle mit 10.000 Zeilen verwendet. Der 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
... dauerte ungefähr 100 Sekunden, um in meiner Testumgebung ausgeführt zu werden.
Im Gegensatz dazu erstellt der folgende Code, der mehrzeilige INSERTs wie oben beschrieben erstellt (unter Verwendung dessen, was Microsoft einen Tabellenwertkonstruktor nennt) ...
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
... dauerte zwischen 1 und 2 Sekunden, um die gleichen Ergebnisse zu erzielen.
(T-SQL-Tabellenwertkonstruktoren sind auf das gleichzeitige Einfügen von 1000 Zeilen beschränkt, daher ist der obige Code etwas komplizierter als sonst.)