Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Como aumentar o desempenho de INSERTs em massa para tabelas vinculadas ODBC no Access?


Essa situação não é incomum ao lidar com INSERTs em massa para tabelas vinculadas ODBC no Access. No caso da seguinte consulta do Access
INSERT INTO METER_DATA (MPO_REFERENCE) 
SELECT MPO_REFERENCE FROM tblTempSmartSSP

onde [METER_DATA] é uma tabela vinculada ao ODBC e [tblTempSmartSSP] é uma tabela de acesso local (nativa), o ODBC é um pouco limitado em quão inteligente pode ser porque deve ser capaz de acomodar uma ampla variedade de bancos de dados de destino cujas capacidades podem variar muito. Infelizmente, muitas vezes isso significa que, apesar da única instrução SQL Access, o que realmente é enviado para o banco de dados remoto (vinculado) é um INSERT separado (ou equivalente) para cada linha na tabela local . Compreensivelmente, isso pode ser muito lento se a tabela local contiver um grande número de linhas.

Opção 1:inserções em massa nativas no banco de dados remoto

Todos os bancos de dados têm um ou mais mecanismos nativos para o carregamento em massa de dados:Microsoft SQL Server tem "bcp" e BULK INSERT , e o Oracle tem "SQL*Loader". Esses mecanismos são otimizados para operações em massa e geralmente oferecem vantagens significativas de velocidade. Na verdade, se os dados precisarem ser importados para o Access e "massageados" antes de serem transferidos para o banco de dados remoto, ainda pode ser mais rápido despejar os dados modificados de volta para um arquivo de texto e importá-los em massa para o banco de dados remoto.

Opção 2:usar uma consulta de passagem no Access

Se os mecanismos de importação em massa não forem uma opção viável, outra possibilidade é criar uma ou mais consultas de passagem no Access para carregar os dados usando instruções INSERT que podem inserir mais de uma linha por vez.

Por exemplo, se o banco de dados remoto fosse SQL Server (2008 ou posterior), poderíamos executar uma consulta de passagem de acesso (T-SQL) como esta
INSERT INTO METER_DATA (MPO_REFERENCE) VALUES (1), (2), (3)

para inserir três linhas com uma instrução INSERT.

De acordo com uma resposta a outra pergunta anterior aqui, a sintaxe correspondente para Oracle seria
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;

Testei essa abordagem com o SQL Server (pois não tenho acesso a um banco de dados Oracle) usando uma tabela nativa [tblTempSmartSSP] com 10.000 linhas. O 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

... levou aproximadamente 100 segundos para ser executado no meu ambiente de teste.

Por outro lado, o código a seguir, que cria INSERTs de várias linhas conforme descrito acima (usando o que a Microsoft chama de construtor de valor de tabela) ...
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

... levou entre 1 e 2 segundos para produzir os mesmos resultados.

(Os construtores de valor de tabela T-SQL estão limitados a inserir 1.000 linhas por vez, portanto, o código acima é um pouco mais complicado do que seria de outra forma.)