9
votes

Le moyen le plus rapide de transférer des données de table Excel sur SQL 2008R2

Est-ce que quelqu'un connaît le moyen le plus rapide d'obtenir des données de la table et de la table Excel (réseau VBA) à une table sur SQL 2008 sans à l'aide d'un utilitaire externe (I.E. BCP)? Gardez à l'esprit que mes jeux de données sont généralement de 6500-15000 rangées et environ 150-250 colonnes; et je finis par transférer environ 20-150 d'entre eux lors d'un script de lot VBA automatisé.

J'ai essayé plusieurs méthodes d'obtenir de grandes quantités de données d'une table Excel (VBA) à SQL 2008. J'ai énuméré ceux ci-dessous:

Méthode 1. Passez la table dans la matrice VBA et envoyez une procédure stockée (ADO) - L'envoi à SQL est lent

Méthode 2. Créez une charge d'enregistrement déconnecté, puis Sync. - Envoi à SQL très lent

Méthode 3. Placez la table dans le tableau VBA, en boucle, bien que le tableau et concaténate (à l'aide de délimiteurs) puis envoyez à la procédure stockée. - Envoi à SQL lent, mais plus rapide que la méthode 1 ou 2.

Méthode 4. Placez la table dans une matrice VBA, en boucle, bien que le tableau et concaténate (à l'aide de délimiteurs) puis placez chaque ligne avec une commande Ado Recokset .AddNew. - Du SQL très rapide (environ 20 fois plus rapide que les méthodes 1 à 3), mais je devrai maintenant diviser ces données à l'aide d'une procédure distincte, qui ajoutera un temps d'attente significatif.

Méthode 5. Placez la table dans le tableau VBA, Serialize dans XML, envoyez une procédure stockée comme Varchar et spécifiez XML dans la procédure stockée. - Du SQL incroyablement lent (environ 100 fois plus lent que les méthodes 1 ou 2)

Tout ce qui me manque?


0 commentaires

6 Réponses :


2
votes

Il n'y a pas de moyen le plus rapide, car il dépend d'un certain nombre de facteurs. Assurez-vous que les index dans SQL sont configurés et optimisés. Beaucoup d'index vont tuer des performances d'insertion / mise à jour car chaque insert devra mettre à jour l'index. Assurez-vous que vous ne faites qu'une connexion à la base de données et n'oubliez pas / fermez-la pendant l'opération. Exécutez la mise à jour lorsque le serveur est sous une charge minimale. La seule autre méthode que vous n'avez pas essayée est d'utiliser un objet de commande ADO et d'émettre une déclaration de insertion directe. Lors de l'utilisation de la méthode "addnew" de l'objet Recordset, assurez-vous d'émettre une seule commande "updatebatch" à la fin des inserts. À court de cela, le VBA ne peut fonctionner que aussi rapidement que le serveur SQL acceptant les entrées.

EDIT: On dirait que tu as tout essayé. Il y a aussi ce que l'on appelle le mode de récupération «enregistré en vrac» dans SQL Server, qui réduit les frais généraux d'enrichir autant au journal des transactions. Pourrait être quelque chose qui vaut la peine d'être examiné. Cela peut être gênant car il nécessite un peu de fiancé avec le modèle de récupération de base de données, mais cela pourrait être utile pour vous.


2 commentaires

-Index ne sont pas un problème parce que je me charge dans une table Temp - mais merci, j'avais oublié ça. En utilisant définitivement une seule connexion. Quant à l'objet de commandement ADO, j'ai essayé cela, mais je l'ai trouvé qu'il n'était pas différent de la vitesse que de passer à un sp. Et oui, je n'ai émis qu'un seul lot ADO Update après avoir une boucle à travers l'ajout de nouveau. La nouvelle méthode .ADD était et est toujours de loin le plus rapide, mais uniquement lorsqu'elle est utilisée conjointement avec le concat - ce qui devrait être analysé plus tard.


Je vais regarder en mode de récupération enregistré en vrac.



0
votes

De loin le moyen le plus rapide de le faire est via la Insert en vrac .

Il y a quelques mises en garde.

  • Vous aurez probablement besoin d'exporter vos données sur un CSV d'abord ( Vous pourrez peut-être importer directement à partir d'Excel; mon expérience consiste à aller de l'accès .MDBS à SQL Server qui nécessite l'étape intermédiaire de CSV ).
  • La machine SQL Server doit avoir accès à ce CSV ( lorsque vous exécutez la commande insert en vrac et spécifiez un nom de fichier, rappelez-vous que le nom de fichier sera résolu sur la machine où SQL Server est en cours d'exécution ).
  • Vous devrez peut-être modifier le fichier fieldterminator et rowterminator pour correspondre à votre CSV.

    Cela a pris des essais et une erreur pour que cela puisse obtenir cela initialement, mais l'augmentation de la performance était phénoménale par rapport à toutes les techniques que j'avais essayées.


2 commentaires

Merci, mais BCP n'est pas une option. Je traite avec des milliers de formats sur la mouche et le BCP ne me donnait que des ennuis que suffisamment pour causer une catastrophe à chaque fois. J'ai besoin de quelque chose que je peux contrôler avec les commentaires d'erreur pendant la boucle du lot; Et cela élimine la plupart des programmes en vrac ... surtout le BCP.


Je n'ai pas réalisé BCP == insert en vrac . Cela dit, j'ai eu des problèmes similaires à travailler avec BCP initialement. Je ne connais pas les spécificités de votre situation, mais j'ai résolu les problèmes que j'avais en utilisant des terminaisons de rangée et de champ personnalisés et de «massage» les données lorsque je l'exporte vers CSV. J'ai essayé des variations de la plupart de ce que vous avez énuméré et que la performance ne s'est jamais rapprochée de l'insert en vrac. Je suis d'accord que l'insert en vrac est "finky" (pour le moins dire) et la rétroaction d'erreur pendant la boucle de lot est à côté de l'impossible (sans avoir recours à une sorte de kludge), mais je pense que ça vaut la peine d'être un autre look. Bonne chance!



2
votes

Le code suivant transférera les milliers de données en quelques secondes seulement (2-3 secondes).

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


2 commentaires

Cela semble écrire des données sur Excel de SQL Server, et non l'inverse, car la question pose la question.


@ ThursdaySgeek Non, cela fait exactement quelle est la question posée.



0
votes

fonctionne assez bien, d'autre part pour améliorer la vitesse, nous pouvons toujours modifier la requête:

à la place: source = "Sélectionnez * à partir de" & Table

Nous pouvons utiliser: source = "Sélectionnez le top 1 * à partir de" & Table

Voici nous n'avons besoin que de noms de colonnes. Donc, pas besoin de maka une requête pour une table entière, qui prolonge le processus tant que de nouvelles données importées.


0 commentaires

0
votes

Autant que je me souvienne, vous pouvez créer un serveur lié au fichier Excel (tant que le serveur peut trouver le chemin; il est préférable de mettre le fichier sur le disque local du serveur), puis d'utiliser SQL pour récupérer des données de ça.


0 commentaires

0
votes

Avoir juste essayé quelques méthodes, je suis revenu à un seul mais rapide. Il est rapide car il fait que le serveur SQL effectue tout le travail, y compris un plan d'exécution efficace.

Je viens de construire une longue chaîne contenant un script d'instructions d'insertion. P>

    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


0 commentaires