Oracle
 sql >> Database >  >> RDS >> Oracle

Come aumentare le prestazioni degli INSERT in blocco alle tabelle collegate ODBC in Access?

Questa situazione non è rara quando si tratta di INSERT in blocco alle tabelle collegate ODBC in Access. Nel caso della seguente query di accesso

INSERT INTO METER_DATA (MPO_REFERENCE) 
SELECT MPO_REFERENCE FROM tblTempSmartSSP

dove [METER_DATA] è una tabella collegata ODBC e [tblTempSmartSSP] è una tabella di Access locale (nativa), ODBC è alquanto limitato in quanto può essere intelligente perché deve essere in grado di ospitare un'ampia gamma di database di destinazione le cui capacità possono variare notevolmente. Sfortunatamente, spesso significa che, nonostante la singola istruzione Access SQL, ciò che viene effettivamente inviato al database remoto (collegato) è un INSERT separato (o equivalente) per ogni riga nella tabella locale . Comprensibilmente, ciò può rivelarsi molto lento se la tabella locale contiene un numero elevato di righe.

Opzione 1:inserimenti collettivi nativi nel database remoto

Tutti i database hanno uno o più meccanismi nativi per il caricamento in blocco dei dati:Microsoft SQL Server ha "bcp" e BULK INSERT e Oracle ha "SQL*Loader". Questi meccanismi sono ottimizzati per operazioni in blocco e di solito offrono vantaggi significativi in ​​termini di velocità. Infatti, se i dati devono essere importati in Access e "massaggiati" prima di essere trasferiti al database remoto, può essere ancora più veloce scaricare nuovamente i dati modificati in un file di testo e quindi importarli in blocco nel database remoto.

Opzione 2:utilizzo di una query pass-through in Access

Se i meccanismi di importazione in blocco non sono un'opzione fattibile, un'altra possibilità consiste nel creare una o più query pass-through in Access per caricare i dati utilizzando istruzioni INSERT che possono inserire più di una riga alla volta.

Ad esempio, se il database remoto era SQL Server (2008 o successivo), potremmo eseguire una query pass-through di accesso (T-SQL) come questa

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

per inserire tre righe con un'istruzione INSERT.

Secondo una risposta a un'altra domanda precedente qui la sintassi corrispondente per Oracle sarebbe

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;

Ho testato questo approccio con SQL Server (poiché non ho accesso a un database Oracle) utilizzando una tabella nativa [tblTempSmartSSP] con 10.000 righe. Il codice...

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

... ci sono voluti circa 100 secondi per l'esecuzione nel mio ambiente di test.

Al contrario, il codice seguente, che crea INSERT a più righe come descritto sopra (usando ciò che Microsoft chiama un costruttore di valori di tabella) ...

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

... ci sono voluti tra 1 e 2 secondi per produrre gli stessi risultati.

(I costruttori di valori di tabella T-SQL sono limitati all'inserimento di 1000 righe alla volta, quindi il codice sopra è un po' più complicato di quanto sarebbe altrimenti.)