2012-05-22 22 views
7

Qualcuno conosce il modo più veloce per ottenere dati da e tabella Excel (array VBA) su una tabella su SQL 2008 senza utilizzando un'utility esterna (ad esempio bcp)? Tenere presente che i miei set di dati sono in genere 6500-15000 righe e circa 150-250 colonne; e finisco per trasferire circa 20-150 di loro durante uno script batch VBA automatizzato.Il modo più veloce per trasferire i dati della tabella Excel su SQL 2008R2

Ho provato diversi metodi per ottenere grandi quantità di dati da una tabella di Excel (VBA) per SQL 2008. Ho elencato quelli sotto:

Metodo tabella 1. passaggio in array VBA e spedire stored procedure (ADO) - L'invio a SQL è SLOW

Metodo 2. Creare RecordSet disconnesso caricarlo, quindi eseguire la sincronizzazione. - Invio a SQL MOLTO LENTO

Metodo 3. Inserire la tabella nell'array VBA, eseguire il ciclo tramite matrice e concatenare (utilizzando delimitatori) quindi inviare alla stored procedure. - invio a SQL SLOW, ma più veloce metodo 1 o 2.

metodo 4. tavolo Mettere in matrice VBA, ciclo se i dell'array e concatenare (utilizzando delimitatori) quindi posizionare ciascuna riga con comando ADO recordset .AddNew. --Sending a SQL molto veloce (circa 20 volte più veloce rispetto ai metodi 1-3), ma ora ho bisogno di dividere i dati utilizzando una procedura separata, che aggiungerà significativo tempo di attesa.

metodo 5. tavolo disposero in ordine di VBA, serializzare in XML, segnala ad stored procedure come VARCHAR e specificare XML in stored procedure. - Invio a SQL INCREDIBILMENTE LENTO (circa 100 volte più lento dei metodi 1 o 2)

Qualcosa che mi manca?

risposta

2

non esiste un unico modo più veloce, come è dipendente da una serie di fattori. Assicurati che gli indici in SQL siano configurati e ottimizzati. Un sacco di indici ucciderà le prestazioni di inserimento/aggiornamento poiché ogni inserto dovrà aggiornare l'indice. Assicurarsi di effettuare una sola connessione al database e non aprirlo/chiuderlo durante l'operazione. Eseguire l'aggiornamento quando il server è sotto carico minimo. L'unico altro metodo che non hai provato è quello di utilizzare un oggetto Command ADO ed emettere un'istruzione INSERT diretta. Quando si utilizza il metodo "AddNew" dell'oggetto recordset, assicurarsi di emettere solo un comando "UpdateBatch" alla fine degli inserti. In mancanza di ciò, il VBA può funzionare solo veloce come il server SQL che accetta gli input.

EDIT: Sembra che tu hai provato di tutto. Esiste anche una modalità di ripristino denominata "Bulk-Logged" in SQL Server, che riduce il sovraccarico di scrittura così tanto sul log delle transazioni. Potrebbe essere qualcosa che vale la pena esaminare. Può essere problematico dal momento che richiede un po 'di manipolazione con il modello di recupero del database, ma potrebbe essere utile per te.

+0

-Indice non sono un problema perché sto caricando in una tabella temporanea - ma grazie, me ne ero dimenticato. Sicuramente usando una sola connessione. Per quanto riguarda l'oggetto comando ADO, l'ho provato, ma ho trovato che non è diverso nella velocità di passare a una sp. E sì, ho emesso un solo batch di aggiornamento ADO dopo aver eseguito il ciclo di aggiunta del nuovo. Il nuovo metodo .add era ed è di gran lunga il più veloce, ma solo se usato in concomitanza con il concat, che dovrebbe essere analizzato in seguito. – cshenderson

+0

Esaminerò la modalità di recupero con registrazione in blocco. – cshenderson

0

Di gran lunga il modo più veloce per farlo è tramite T-SQL di BULK INSERT.

Ci sono alcune avvertenze.

  • È probabile che sia necessario esportare i dati in un file CSV prima (si può essere in grado di importare direttamente da Excel, la mia esperienza è in corso da Access .mdbs a SQL Server che richiede il passaggio intermedio in formato CSV).
  • Il computer SQL Server deve avere accesso a tale csv (quando si esegue il comando BULK INSERT e si specifica un nome file, ricordare che il nome file verrà risolto sulla macchina su cui è in esecuzione SQL Server).
  • Potrebbe essere necessario modificare i valori predefiniti FIELDTERMINATOR e ROWTERMINATOR in modo che corrispondano al tuo CSV.

Ci sono voluti alcuni tentativi per ottenere questa configurazione inizialmente, ma l'aumento delle prestazioni è stato fenomenale rispetto a tutte le altre tecniche che avevo provato.

+0

Grazie, ma BCP non è un'opzione. Mi occupo di migliaia di formati al volo e bcp non mi ha dato altro che problemi su un numero sufficiente di loro per causare ogni volta un disastro. Ho bisogno di qualcosa che posso controllare con feedback degli errori durante il ciclo batch; e questo elimina la maggior parte dei programmi bulk ... specialmente bcp. – cshenderson

+0

Non ho realizzato BCP == 'BULK INSERT'. Detto questo, ho avuto problemi simili io stesso lavorando inizialmente con BCP. Non conosco le specifiche della tua situazione, ma ho risolto i problemi che avevo utilizzando i terminatori personalizzati di riga e campo e "massaggiando" i dati quando li ho esportati in CSV. Ho provato le variazioni della maggior parte di ciò che hai elencato e le prestazioni non si sono mai avvicinate al Bulk Insert. Sono d'accordo sul fatto che Bulk Insert è "schizzinoso" (per non dire altro) e il feedback degli errori durante il ciclo batch è quasi impossibile (senza ricorrere a qualche sorta di kludge), ma penso che valga la pena di dare un'altra occhiata. Buona fortuna! – mwolfe02

2

Il seguente codice trasferirà le migliaia di dati in pochi secondi (2-3 secondi).

Dim sheet As Worksheet 
    Set sheet = ThisWorkbook.Sheets("DataSheet")   

    Dim Con As Object 
    Dim cmd As Object 
    Dim ServerName As String 
    Dim level As Long 
    Dim arr As Variant 
    Dim row As Long 
    Dim rowCount As Long 

    Set Con = CreateObject("ADODB.Connection") 
    Set cmd = CreateObject("ADODB.Command") 

    ServerName = "192.164.1.11" 

    'Creating a connection 
    Con.ConnectionString = "Provider=SQLOLEDB;" & _ 
            "Data Source=" & ServerName & ";" & _ 
            "Initial Catalog=Adventure;" & _ 
            "UID=sa; PWD=123;" 

    'Setting provider Name 
    Con.Provider = "Microsoft.JET.OLEDB.12.0" 

    'Opening connection 
    Con.Open     

    cmd.CommandType = 1    ' adCmdText 

    Dim Rst As Object 
    Set Rst = CreateObject("ADODB.Recordset") 
    Table = "EmployeeDetails" 'This should be same as the database table name. 
    With Rst 
     Set .ActiveConnection = Con 
     .Source = "SELECT * FROM " & Table 
     .CursorLocation = 3   ' adUseClient 
     .LockType = 4    ' adLockBatchOptimistic 
     .CursorType = 0    ' adOpenForwardOnly 
     .Open 

     Dim tableFields(200) As Integer 
     Dim rangeFields(200) As Integer 

     Dim exportFieldsCount As Integer 
     exportFieldsCount = 0 

     Dim col As Integer 
     Dim index As Integer 
     index = 1 

     For col = 1 To .Fields.Count 
      exportFieldsCount = exportFieldsCount + 1 
      tableFields(exportFieldsCount) = col 
      rangeFields(exportFieldsCount) = index 
      index = index + 1 
     Next 

     If exportFieldsCount = 0 Then 
      ExportRangeToSQL = 1 
      GoTo ConnectionEnd 
     End If    

     endRow = ThisWorkbook.Sheets("DataSheet").Range("A65536").End(xlUp).row 'LastRow with the data. 
     arr = ThisWorkbook.Sheets("DataSheet").Range("A1:CE" & endRow).Value 'This range selection column count should be same as database table column count. 

     rowCount = UBound(arr, 1)    

     Dim val As Variant 

     For row = 1 To rowCount 
      .AddNew 
      For col = 1 To exportFieldsCount 
       val = arr(row, rangeFields(col)) 
        .Fields(tableFields(col - 1)) = val 
      Next 
     Next 

     .UpdateBatch 
    End With 

    flag = True 

    'Closing RecordSet. 
    If Rst.State = 1 Then 
     Rst.Close 
    End If 

    'Closing Connection Object. 
    If Con.State = 1 Then 
     Con.Close 
    End If 

'Setting empty for the RecordSet & Connection Objects 
Set Rst = Nothing 
Set Con = Nothing 
End Sub 
+0

Sembra che stia scrivendo dati su Excel da SQL Server, non viceversa, come la domanda sta ponendo. – thursdaysgeek

+1

@thursdaysgeek no, questo fa esattamente quello che la domanda si pone. –

0

funziona abbastanza bene, d'altra parte per migliorare la velocità possiamo ancora modificare la query:

Invece: Source = "SELECT * FROM " & Table

possiamo usare: Source = "SELECT TOP 1 * FROM " & Table

Qui è abbiamo solo bisogno nomi di colonne. Quindi non c'è bisogno di eseguire una query per l'intera tabella, che estende il processo fino a quando i nuovi dati vengono importati.

0

Per quanto mi ricordo, è possibile creare un server collegato al file Excel (purché il server possa trovare il percorso, è meglio mettere il file sul disco locale del server) e quindi utilizzare SQL per recuperare i dati da.

0

Avendo appena provato alcuni metodi, sono tornato a uno relativamente semplice ma veloce. È veloce perché fa tutto il lavoro del server SQL, incluso un efficiente piano di esecuzione.

Ho appena creato una lunga stringa contenente uno script di istruzioni INSERT.

Public Sub Upload() 
     Const Tbl As String = "YourTbl" 
     Dim InsertQuery As String, xlRow As Long, xlCol As Integer 
     Dim DBconnection As New ADODB.Connection 

     DBconnection.Open "Provider=SQLOLEDB.1;Password=MyPassword" & _ 
      ";Persist Security Info=false;User ID=MyUserID" & _ 
      ";Initial Catalog=MyDB;Data Source=MyServer" 

     InsertQuery = "" 
     xlRow = 2 
     While Cells(xlRow, 1) <> "" 
      InsertQuery = InsertQuery & "INSERT INTO " & Tbl & " VALUES('" 

      For xlCol = 1 To 6 'Must match the table structure 
       InsertQuery = InsertQuery & Replace(Cells(xlRow, xlCol), "'", "''") & "', '" 'Includes mitigation for apostrophes in the data 
      Next xlCol 
      InsertQuery = InsertQuery & Format(Now(), "M/D/YYYY") & "')" & vbCrLf 'The last column is a date stamp, either way, don't forget to close that parenthesis 
      xlRow = xlRow + 1 
     Wend 

     DBconnection.Execute InsertQuery 'I'll leave any error trapping to you 
     DBconnection.Close 'But do be tidy :-) 
     Set DBconnection = Nothing 
    End Sub 
Problemi correlati