Cette situation n'est pas rare lorsqu'il s'agit d'INSERTS en masse vers des tables liées ODBC dans Access. Dans le cas de la requête Access suivante
INSERT INTO METER_DATA (MPO_REFERENCE)
SELECT MPO_REFERENCE FROM tblTempSmartSSP
où [METER_DATA] est une table liée ODBC et [tblTempSmartSSP] est une table d'accès locale (native), ODBC est quelque peu limité dans son intelligence car il doit être capable de prendre en charge un large éventail de bases de données cibles dont les capacités peuvent varier très. Malheureusement, cela signifie souvent que malgré l'unique instruction Access SQL, ce qui est réellement envoyé à la base de données distante (liée) est un INSERT séparé (ou équivalent) pour chaque ligne de la table locale . Naturellement, cela peut s'avérer très lent si la table locale contient un grand nombre de lignes.
Option 1 :Insertions en masse natives dans la base de données distante
Toutes les bases de données ont un ou plusieurs mécanismes natifs pour le chargement en masse des données :Microsoft SQL Server a "bcp" et BULK INSERT
, et Oracle a "SQL*Loader". Ces mécanismes sont optimisés pour les opérations en vrac et offrent généralement des avantages de vitesse significatifs. En fait, si les données doivent être importées dans Access et "massées" avant d'être transférées vers la base de données distante, il peut encore être plus rapide de vider les données modifiées dans un fichier texte, puis de les importer en masse dans la base de données distante.
Option 2 :Utiliser une requête directe dans Access
Si les mécanismes d'importation en bloc ne sont pas une option réalisable, une autre possibilité consiste à créer une ou plusieurs requêtes directes dans Access pour télécharger les données à l'aide d'instructions INSERT qui peuvent insérer plusieurs lignes à la fois.
Par exemple, si la base de données distante était SQL Server (2008 ou version ultérieure), nous pourrions exécuter une requête Access pass-through (T-SQL) comme celle-ci
INSERT INTO METER_DATA (MPO_REFERENCE) VALUES (1), (2), (3)
pour insérer trois lignes avec une instruction INSERT.
Selon une réponse à une autre question précédente ici, la syntaxe correspondante pour Oracle serait
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;
J'ai testé cette approche avec SQL Server (car je n'ai pas accès à une base de données Oracle) en utilisant une table native [tblTempSmartSSP] avec 10 000 lignes. Le 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
... a pris environ 100 secondes pour s'exécuter dans mon environnement de test.
En revanche, le code suivant, qui construit des INSERTs multi-lignes comme décrit ci-dessus (en utilisant ce que Microsoft appelle un constructeur de valeur de table) ...
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
... a pris entre 1 et 2 secondes pour produire les mêmes résultats.
(Les constructeurs de valeurs de table T-SQL sont limités à l'insertion de 1000 lignes à la fois, donc le code ci-dessus est un peu plus compliqué qu'il ne le serait autrement.)