Ta sytuacja nie jest rzadkością, gdy mamy do czynienia z masowymi wstawkami do tabel połączonych ODBC w programie Access. W przypadku następującego zapytania dostępu
INSERT INTO METER_DATA (MPO_REFERENCE)
SELECT MPO_REFERENCE FROM tblTempSmartSSP
gdzie [METER_DATA] to połączona tabela ODBC, a [tblTempSmartSSP] to lokalna (natywna) tabela dostępu, ODBC jest nieco ograniczone pod względem tego, jak sprytny może być, ponieważ musi być w stanie obsłużyć szeroką gamę docelowych baz danych, których możliwości mogą się różnić bardzo. Niestety często oznacza to, że pomimo pojedynczej instrukcji Access SQL to, co faktycznie jest wysyłane do zdalnej (połączonej) bazy danych, to osobna INSERT (lub odpowiednik) dla każdego wiersza w lokalnej tabeli . Zrozumiałe, że może to być bardzo powolne, jeśli tabela lokalna zawiera dużą liczbę wierszy.
Opcja 1:natywne wstawianie zbiorcze do zdalnej bazy danych
Wszystkie bazy danych mają co najmniej jeden natywny mechanizm zbiorczego ładowania danych:Microsoft SQL Server ma „bcp” i BULK INSERT
, a Oracle ma "SQL*Loader". Mechanizmy te są zoptymalizowane pod kątem operacji masowych i zwykle oferują znaczne korzyści w zakresie szybkości. W rzeczywistości, jeśli dane muszą zostać zaimportowane do programu Access i „masowane” przed przesłaniem do zdalnej bazy danych, nadal może być szybciej zrzucić zmodyfikowane dane z powrotem do pliku tekstowego, a następnie zbiorczo zaimportować je do zdalnej bazy danych.
Opcja 2:używanie zapytania przekazującego w programie Access
Jeśli mechanizmy importu zbiorczego są niewykonalne, inną możliwością jest utworzenie co najmniej jednego zapytania przekazującego w programie Access w celu przesłania danych przy użyciu instrukcji INSERT, które mogą wstawiać jednocześnie więcej niż jeden wiersz.
Na przykład, jeśli zdalną bazą danych był SQL Server (2008 lub nowszy), moglibyśmy uruchomić zapytanie tranzytowe Access (T-SQL) w ten sposób
INSERT INTO METER_DATA (MPO_REFERENCE) VALUES (1), (2), (3)
aby wstawić trzy wiersze z jedną instrukcją INSERT.
Zgodnie z odpowiedzią na inne wcześniejsze pytanie, odpowiednia składnia Oracle byłaby
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;
Testowałem to podejście z SQL Server (ponieważ nie mam dostępu do bazy danych Oracle) przy użyciu natywnej tabeli [tblTempSmartSSP] z 10 000 wierszy. Kod ...
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
... wykonanie w moim środowisku testowym zajęło około 100 sekund.
Natomiast poniższy kod, który buduje wielowierszowe INSERT, jak opisano powyżej (używając tego, co Microsoft nazywa konstruktorem wartości tabeli) ...
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
... uzyskanie tych samych wyników zajęło od 1 do 2 sekund.
(Konstruktory wartości tabel T-SQL są ograniczone do wstawiania 1000 wierszy na raz, więc powyższy kod jest nieco bardziej skomplikowany, niż byłby w innym przypadku).