oracle ms-access odbc access-vba ms-access-2003

oracle - ¿Cómo aumentar el rendimiento de INSERT a granel en las tablas vinculadas de ODBC en Access?



ms-access access-vba (3)

¿Tienes que importar todas las columnas? Tal vez quieras omitir las columnas vacías, si hay alguna; y también columnas que no son absolutamente necesarias para fines comerciales

Tengo archivos CSV y TXT para importar. Estoy importando los archivos en Access y luego inserto los registros en una tabla de Oracle vinculada. Cada archivo tiene alrededor de 3 millones de filas y el proceso tarda mucho tiempo en completarse.

Importar en Access es muy rápido, pero insertarlo en la tabla de Oracle vinculada lleva mucho tiempo.

Este es el proceso que estoy usando actualmente:

DoCmd.TransferText acImportFixed, "BUSSEP2014 Link Specification", "tblTempSmartSSP", strFName, False db.Execute "INSERT INTO METER_DATA ([MPO_REFERENCE]) SELECT MPO_REFERENCE FROM tblTempSmartSSP;"`

tblTempSmartSSP es una tabla de acceso y METER_DATA es una tabla de Oracle vinculada

También intenté importar directamente a la tabla vinculada y eso también fue muy lento.

¿Cómo puedo acelerar el proceso?


Esta situación no es poco común cuando se trata de INSERT a granel en tablas vinculadas a ODBC en Access. En el caso de la siguiente consulta de Access

INSERT INTO METER_DATA (MPO_REFERENCE) SELECT MPO_REFERENCE FROM tblTempSmartSSP

donde [METER_DATA] es una tabla vinculada a 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 acomodar una amplia gama de bases de datos de destino cuyas capacidades pueden variar muy. Desafortunadamente, a menudo significa que, a pesar de la sentencia única de Access SQL, lo que en realidad se envía a la base de datos remota (vinculada) es un INSERT por separado (o equivalente) para cada fila en la tabla local . Es comprensible que pueda ser muy lento si la tabla local contiene una gran cantidad de filas.

Opción 1: inserciones nativas a granel 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 masivas y generalmente ofrecerán ventajas de velocidad significativas. De hecho, si los datos deben importarse en Access y "masajearse" antes de ser transferidos a la base de datos remota, puede ser más rápido volcar los datos modificados a un archivo de texto y luego importarlos a granel en la base de datos remota.

Opción 2: usar una consulta de paso a través en Access

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

Por ejemplo, si la base de datos remota era SQL Server (2008 o posterior), entonces podríamos ejecutar una consulta de acceso de acceso (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 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

... demoré aproximadamente 100 segundos en ejecutar en mi entorno de prueba.

Por el contrario, el siguiente código, que crea INSERTs de varias filas como se describió anteriormente (usando lo que Microsoft llama un Constructor de valores 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 están limitados 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).


Lo siento, olvidé incluir el código:

Option Compare Database Option Explicit Public Function Run_Safe_SQL(strSQL) On Error GoTo Error_Handler Dim db As DAO.Database Set db = CurrentDb() db.Execute strSQL, dbFailOnError DBEngine.Idle dbRefreshCache '' DoEvents Exit_Here: ''Cleanup Set db = Nothing strSQL = "" Exit Function Error_Handler: MsgBox Err.Description & " " & Err.Number End Function