sql >> Base de Datos >  >> RDS >> Oracle

¿Cómo aumentar el rendimiento de INSERT masivos en tablas vinculadas ODBC en Access?

Esta situación no es infrecuente cuando se trata de INSERCIONES masivas en tablas vinculadas ODBC en Access. En el caso de la siguiente consulta de acceso

INSERT INTO METER_DATA (MPO_REFERENCE) 
SELECT MPO_REFERENCE FROM tblTempSmartSSP

donde [METER_DATA] es una tabla vinculada de ODBC y [tblTempSmartSSP] es una tabla de acceso local (nativa), ODBC está algo limitado en lo inteligente que puede ser porque tiene que ser capaz de adaptarse a una amplia gama de bases de datos de destino cuyas capacidades pueden variar. muy. Desafortunadamente, a menudo significa que, a pesar de la única instrucción SQL de Access, lo que realmente se envía a la base de datos remota (vinculada) es un INSERT (o equivalente) separado para cada fila en la tabla local . Comprensiblemente, eso puede resultar muy lento si la tabla local contiene una gran cantidad de filas.

Opción 1:inserciones masivas nativas en la base de datos remota

Todas las bases de datos tienen uno o más mecanismos nativos para la carga masiva de datos:Microsoft SQL Server tiene "bcp" y BULK INSERT y Oracle tiene "SQL*Loader". Estos mecanismos están optimizados para operaciones a granel y, por lo general, ofrecerán importantes ventajas de velocidad. De hecho, si es necesario importar los datos a Access y "masajearlos" antes de transferirlos a la base de datos remota, aún puede ser más rápido volcar los datos modificados en un archivo de texto y luego importarlos en masa a la base de datos remota.

Opción 2:usar una consulta de transferencia en Access

Si los mecanismos de importación masiva no son una opción factible, otra posibilidad es crear una o más consultas de transferencia en Access para cargar los datos mediante instrucciones INSERT que pueden insertar más de una fila a la vez.

Por ejemplo, si la base de datos remota fuera SQL Server (2008 o posterior), podríamos ejecutar una consulta de paso a través de Access (T-SQL) como esta

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

para insertar tres filas con una instrucción INSERT.

Según una respuesta a otra pregunta anterior aquí, la sintaxis correspondiente para Oracle sería

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;

Probé este enfoque con SQL Server (ya que no tengo acceso a una base de datos de Oracle) usando una tabla nativa [tblTempSmartSSP] con 10 000 filas. El código...

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

... tardó aproximadamente 100 segundos en ejecutarse en mi entorno de prueba.

Por el contrario, el siguiente código, que crea INSERTOS de varias filas como se describe anteriormente (usando lo que Microsoft llama un Constructor de valor de tabla) ...

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

... tomó entre 1 y 2 segundos para producir los mismos resultados.

(Los constructores de valores de tabla T-SQL se limitan a insertar 1000 filas a la vez, por lo que el código anterior es un poco más complicado de lo que sería de otra manera).