sql >> Database >  >> RDS >> Oracle

Hoe de prestaties voor bulk-INSERT's naar ODBC-gekoppelde tabellen in Access te verbeteren?

Deze situatie is niet ongebruikelijk bij het verwerken van bulk INSERT's naar ODBC gekoppelde tabellen in Access. In het geval van de volgende Access-query

INSERT INTO METER_DATA (MPO_REFERENCE) 
SELECT MPO_REFERENCE FROM tblTempSmartSSP

waar [METER_DATA] een ODBC-gekoppelde tabel is en [tblTempSmartSSP] een lokale (native) Access-tabel is, is ODBC enigszins beperkt in hoe slim het kan zijn omdat het een breed scala aan doeldatabases moet kunnen accommoderen waarvan de mogelijkheden kunnen variëren enorm. Helaas betekent dit vaak dat, ondanks de enkele Access SQL-instructie, er een aparte INSERT (of equivalent) voor elke rij in de lokale tabel wordt verzonden naar de externe (gekoppelde) database. . Het is begrijpelijk dat dit erg traag kan zijn als de lokale tabel een groot aantal rijen bevat.

Optie 1:Native bulk-inserts naar de externe database

Alle databases hebben een of meer native mechanismen voor het bulksgewijs laden van gegevens:Microsoft SQL Server heeft "bcp" en BULK INSERT , en Oracle heeft "SQL*Loader". Deze mechanismen zijn geoptimaliseerd voor bulkoperaties en bieden doorgaans aanzienlijke snelheidsvoordelen. Als de gegevens moeten worden geïmporteerd in Access en moeten worden "gemasseerd" voordat ze worden overgebracht naar de externe database, kan het nog steeds sneller zijn om de gewijzigde gegevens terug naar een tekstbestand te dumpen en deze vervolgens in bulk in de externe database te importeren.

Optie 2:Een pass-through-query gebruiken in Access

Als de bulkimportmechanismen geen haalbare optie zijn, is een andere mogelijkheid om een ​​of meer pass-through-query's in Access te bouwen om de gegevens te uploaden met behulp van INSERT-instructies die meer dan één rij tegelijk kunnen invoegen.

Als de externe database bijvoorbeeld SQL Server (2008 of later) was, zouden we een Access Pass-Through (T-SQL)-query zoals deze kunnen uitvoeren

INSERT INTO METER_DATA (MPO_REFERENCE) VALUES (1), (2), (3)

om drie rijen met één INSERT-instructie in te voegen.

Volgens een antwoord op een andere eerdere vraag hier zou de corresponderende syntaxis voor Oracle zijn

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;

Ik heb deze aanpak getest met SQL Server (omdat ik geen toegang heb tot een Oracle-database) met behulp van een native [tblTempSmartSSP]-tabel met 10.000 rijen. De 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

... duurde ongeveer 100 seconden om uit te voeren in mijn testomgeving.

De volgende code daarentegen, die INSERT's met meerdere rijen bouwt zoals hierboven beschreven (met behulp van wat Microsoft een Table Value Constructor noemt) ...

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

... duurde tussen de 1 en 2 seconden om dezelfde resultaten te produceren.

(T-SQL Table Value Constructors zijn beperkt tot het invoegen van 1000 rijen tegelijk, dus de bovenstaande code is iets gecompliceerder dan anders het geval zou zijn.)



  1. Hoe een SSIS-pakket uit te voeren vanuit .NET?

  2. Een selectie maken met array bevat waardeclausule in psql

  3. Slaapstand:Mysql InnoDB-tabellen maken in plaats van MyISAM

  4. 3 manieren om de positie van een subtekenreeks binnen een tekenreeks in MySQL te vinden