Denna situation är inte ovanlig när man hanterar bulk INSERTs till ODBC-länkade tabeller i Access. I fallet med följande Access-fråga
INSERT INTO METER_DATA (MPO_REFERENCE)
SELECT MPO_REFERENCE FROM tblTempSmartSSP
där [METER_DATA] är en ODBC-länkad tabell och [tblTempSmartSSP] är en lokal (native) Access-tabell, är ODBC något begränsad i hur smart den kan vara eftersom den måste kunna ta emot ett brett utbud av måldatabaser vars kapacitet kan variera mycket. Tyvärr betyder det ofta att trots den enda Access SQL-satsen vad som faktiskt skickas till den fjärranslutna (länkade) databasen är en separat INSERT (eller motsvarande) för varje rad i den lokala tabellen . Förståeligt nog kan det visa sig vara väldigt långsamt om den lokala tabellen innehåller ett stort antal rader.
Alternativ 1:Inbyggda massinlägg till fjärrdatabasen
Alla databaser har en eller flera inbyggda mekanismer för bulkladdning av data:Microsoft SQL Server har "bcp" och BULK INSERT
, och Oracle har "SQL*Loader". Dessa mekanismer är optimerade för bulkoperationer och kommer vanligtvis att erbjuda betydande hastighetsfördelar. Faktum är att om data behöver importeras till Access och "masseras" innan de överförs till fjärrdatabasen kan det fortfarande gå snabbare att dumpa den modifierade informationen tillbaka till en textfil och sedan massimportera den till fjärrdatabasen.
Alternativ 2:Använda en genomkopplingsfråga i Access
Om massimportmekanismerna inte är ett genomförbart alternativ, är en annan möjlighet att bygga en eller flera genomkopplingsfrågor i Access för att ladda upp data med hjälp av INSERT-satser som kan infoga mer än en rad åt gången.
Till exempel, om fjärrdatabasen var SQL Server (2008 eller senare) skulle vi kunna köra en Access pass-through-fråga (T-SQL) så här
INSERT INTO METER_DATA (MPO_REFERENCE) VALUES (1), (2), (3)
för att infoga tre rader med en INSERT-sats.
Enligt ett svar på en annan tidigare fråga här skulle motsvarande syntax för Oracle vara
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;
Jag testade detta tillvägagångssätt med SQL Server (eftersom jag inte har tillgång till en Oracle-databas) med en inbyggd [tblTempSmartSSP]-tabell med 10 000 rader. 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 ungefär 100 sekunder att köra i min testmiljö.
Däremot följande kod, som bygger INSERT med flera rader enligt beskrivningen ovan (med vad Microsoft kallar 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 mellan 1 och 2 sekunder att producera samma resultat.
(T-SQL-tabellvärdekonstruktörer är begränsade till att infoga 1000 rader åt gången, så ovanstående kod är lite mer komplicerad än den skulle vara annars.)