2010-04-22 4 views
5

Je cherche le moyen le plus rapide de charger des données en masse via C#. J'ai ce script qui fait le travail mais lentement. J'ai lu des témoignages que SqlBulkCopy est le plus rapide.
1000 enregistrements 2,5 secondes. les fichiers contiennent n'importe où près de 5000 enregistrements à 250k Quelles sont certaines des choses qui peuvent ralentir?SqlBulkCopy lent comme la mélasse

Tableau Def:

CREATE TABLE [dbo].[tempDispositions](
    [QuotaGroup] [varchar](100) NULL, 
    [Country] [varchar](50) NULL, 
    [ServiceGroup] [varchar](50) NULL, 
    [Language] [varchar](50) NULL, 
    [ContactChannel] [varchar](10) NULL, 
    [TrackingID] [varchar](20) NULL, 
    [CaseClosedDate] [varchar](25) NULL, 
    [MSFTRep] [varchar](50) NULL, 
    [CustEmail] [varchar](100) NULL, 
    [CustPhone] [varchar](100) NULL, 
    [CustomerName] [nvarchar](100) NULL, 
    [ProductFamily] [varchar](35) NULL, 
    [ProductSubType] [varchar](255) NULL, 
    [CandidateReceivedDate] [varchar](25) NULL, 
    [SurveyMode] [varchar](1) NULL, 
    [SurveyWaveStartDate] [varchar](25) NULL, 
    [SurveyInvitationDate] [varchar](25) NULL, 
    [SurveyReminderDate] [varchar](25) NULL, 
    [SurveyCompleteDate] [varchar](25) NULL, 
    [OptOutDate] [varchar](25) NULL, 
    [SurveyWaveEndDate] [varchar](25) NULL, 
    [DispositionCode] [varchar](5) NULL, 
    [SurveyName] [varchar](20) NULL, 
    [SurveyVendor] [varchar](20) NULL, 
    [BusinessUnitName] [varchar](25) NULL, 
    [UploadId] [int] NULL, 
    [LineNumber] [int] NULL, 
    [BusinessUnitSubgroup] [varchar](25) NULL, 
    [FileDate] [datetime] NULL 
) ON [PRIMARY] 

et voici le code

private void BulkLoadContent(DataTable dt) 
    { 
     OnMessage("Bulk loading records to temp table"); 
     OnSubMessage("Bulk Load Started"); 
     using (SqlBulkCopy bcp = new SqlBulkCopy(conn)) 
     { 
      bcp.DestinationTableName = "dbo.tempDispositions"; 
      bcp.BulkCopyTimeout = 0; 
      foreach (DataColumn dc in dt.Columns) 
      { 
       bcp.ColumnMappings.Add(dc.ColumnName, dc.ColumnName); 
      } 
      bcp.NotifyAfter = 2000; 
      bcp.SqlRowsCopied += new SqlRowsCopiedEventHandler(bcp_SqlRowsCopied); 
      bcp.WriteToServer(dt); 
      bcp.Close(); 
     } 
    } 
+1

Lent en comparaison de quoi? Combien d'enregistrements? Quelles autres approches avez-vous essayé? Est-ce "lent" comme dans "gee, je ferais mieux d'aller prendre du café" ou comme dans "je pourrais le saisir à la main plus vite que cela"? – GalacticCowboy

+0

Lent comme dans "Je ferais mieux d'aller prendre du café" 1000 dossiers 2,5 secondes. les fichiers contiennent n'importe où près de 5000 enregistrements à 250k –

+0

Votre base de données dispose-t-elle d'une fonction LOAD DATA INFILE et pouvez-vous l'appeler directement? – dnagirl

Répondre

7

Avez-vous des index, triggers ou contraintes sur cette table?

Cela entraînera des ralentissements lors de l'insertion - en particulier un index clusterisé pourrait en souffrir. Lors de l'explosion de la quantité de données que vous êtes en train de faire, il est préférable de déposer d'abord les index et de les réappliquer par la suite.

Un bon post à ce sujet est ici: What's the fastest way to bulk insert a lot of data in SQL Server (C# client)

1

Les choses qui peuvent ralentir la copie en bloc: index de texte -Full sur la table -Triggers sur Insérer contraintes-clés

-Langues étrangères
3

Si vous avez beaucoup de données, définir la taille de lot à un nombre raisonnablement grand peut aider:

bcp.BatchSize = 10000; 
0

L'implémentation IDataReader que je suggère ested ici How to implement IDataReader? peut-être vous aide. Je l'ai utilisé avec SqlBulkCopy comme suit:

using (MyFileDataReader reader = new MyFileDataReader(@"C:\myfile.txt")) 
{ 
     SqlBulkCopy bulkCopy = new SqlBulkCopy(connection); 
     bulkCopy.DestinationTableName = "[my_table]"; 
     bulkCopy.BatchSize = 10000; 

     bulkCopy.WriteToServer(reader); 

     bulkCopy.Close(); 

} 
1

J'ai remarqué que d'essayer de débusquer les grands ensembles de données est d'abord beaucoup plus rapide, mais ralentit considérablement au fil du temps. J'ai trouvé une augmentation modeste de la performance en utilisant une approche tamponnée, alimentant en bloc quelques milliers d'enregistrements à la fois sous la même connexion. Il semble que le temps de transaction par lot diminue au fil du temps, ce qui (au fil du temps) améliore les performances. Sur ma solution, j'ai noté que la même méthode non-tamponnée permettra d'économiser environ 5.000.000 d'enregistrements dans le temps qu'il faut à cette méthode pour enregistrer environ 7.500.000 enregistrements du même type à la même DB. Espérons que cela aide quelqu'un.

public void flush_DataTable(DataTable dt, string tableName)//my incoming DTs have a million or so each and slow down over time to nothing. This helps. 
    { int bufferSize = 10000; 
     int bufferHigh = bufferSize; 
     int lowBuffer = 0; 
     if (dt.Rows.Count >= bufferSize) 
     { using (SqlConnection conn = getConn()) 
      { conn.Open(); 
       while (bufferHigh < dt.Rows.Count) 
       { 
        using (SqlBulkCopy s = new SqlBulkCopy(conn)) 
        { s.BulkCopyTimeout = 900; 
         s.DestinationTableName = tableName; 
         s.BatchSize = bufferSize; 

         s.EnableStreaming = true; 
         foreach (var column in dt.Columns) 
          s.ColumnMappings.Add(column.ToString(), column.ToString()); 
         DataTable bufferedTable = dt.Clone(); 
         for (int bu = lowBuffer; bu < bufferHigh; bu++) 
         { 
          bufferedTable.ImportRow(dt.Rows[bu]); 
         } 
         s.WriteToServer(bufferedTable); 
         if (bufferHigh == dt.Rows.Count) 
         { 
          break; 
         } 
         lowBuffer = bufferHigh; 
         bufferHigh += bufferSize; 

         if (bufferHigh > dt.Rows.Count) 
         { 
          bufferHigh = dt.Rows.Count; 
         } 
        } 
       } 
       conn.Close(); 
      } 
     } 
     else 
     { 
      flushDataTable(dt, tableName);//perofrm a non-buffered flush (could just as easily flush the buffer here bu I already had the other method 
     } 
    } 
+0

Vous pourriez peut-être accélérer les choses en utilisant un Parallel.For pour la copie de la table et/ou en profitant de l'enfilage en autorisant 2-3 insertions parallèles en utilisant la fonction wait.SoWriteToServerAsync. Vous pouvez également essayer de placer la logique de boucle (WHILE) dans le mappage de colonne Utiliser et après, afin que tout se produise sous la même instance de SQLBulkCopy - J'étais assez content de l'augmentation de 50% des performances au fil du temps de ces autres astuces. YMMV –

+1

Aussi - c'est plus lent quand une table est vide - les gains de performance semblent se produire à mesure que la table se remplit. Pour quelques millions de disques, je doute que cela soit d'une grande utilité. Mais pour des centaines de millions, cela m'a sauvé beaucoup de temps. –