Oracle
 sql >> Base de données >  >> RDS >> Oracle

Comment augmenter les performances des INSERTs en masse vers les tables liées ODBC dans Access ?

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.)