Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Wie kann die Leistung für Massen-INSERTs in mit ODBC verknüpfte Tabellen in Access gesteigert werden?

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