2010-08-02 5 views
1

J'utilise SQLBULKCOPY pour copier certaines tables de données dans une table de base de données, cependant, parce que la taille des fichiers que je copie s'exécute parfois au-delà de 600 Mo, je manque de mémoire. Je souhaite obtenir quelques conseils sur la gestion de la taille de la table avant de la valider dans la base de données afin de libérer de la mémoire pour continuer à écrire.sqlbulkcopy mem. gestion

Voici quelques exemples de mon code (quelques colonnes et de lignes éliminées pour la simplicité)

  SqlBulkCopy sqlbulkCopy = new SqlBulkCopy(ServerConfiguration); //Define the Server Configuration 
     System.IO.StreamReader rdr = new System.IO.StreamReader(fileName); 

     Console.WriteLine("Counting number of lines..."); 
     Console.WriteLine("{0}, Contains: {1} Lines", fileName, countLines(fileName)); 

     DataTable dt = new DataTable(); 

     sqlbulkCopy.DestinationTableName = "[dbo].[buy.com]"; //You need to define the target table name where the data will be copied 
     dt.Columns.Add("PROGRAMNAME"); 
     dt.Columns.Add("PROGRAMURL"); 
     dt.Columns.Add("CATALOGNAME"); 

     string inputLine = ""; 
     DataRow row; //Declare a row, which will be added to the above data table 

     while ((inputLine = rdr.ReadLine()) != null) //Read while the line is not null 
      { 
       i = 0; 
       string[] arr; 

       Console.Write("\rWriting Line: {0}", k); 
       arr = inputLine.Split('\t'); //splitting the line which was read by the stream reader object (tab delimited) 
       row = dt.NewRow(); 
       row["PROGRAMNAME"] = arr[i++]; 
       row["PROGRAMURL"] = arr[i++]; 
       row["CATALOGNAME"] = arr[i++]; 
       row["LASTUPDATED"] = arr[i++]; 
       row["NAME"] = arr[i++]; 
       dt.Rows.Add(row); 
       k++; 
     } 

     // Set the timeout, 600 secons (10 minutes) given table size--damn that's a lota hooch 
     sqlbulkCopy.BulkCopyTimeout = 600; 
     try 
     { 
      sqlbulkCopy.WriteToServer(dt); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(e); 
     } 
     sqlbulkCopy.Close();//Release the resources 
     dt.Dispose(); 

     Console.WriteLine("\nDB Table Written: \"{0}\" \n\n", sqlbulkCopy.DestinationTableName.ToString()); 

    } 

je continuais à avoir des problèmes se SqlBulkCopy à travailler, et je réalise que je devais faire plus travailler chaque enregistrement avant qu'il a été entré dans la base de données, donc j'ai développé une méthode simple LinQ à Sql pour enregistrer par des mises à jour d'enregistrement, afin que je puisse éditer d'autres informations et créer plus d'informations en cours d'exécution,

Problème: This méthode a été en cours d'exécution jolie slo w (même sur machine Core i3), des idées sur la façon de l'accélérer (threading?) - sur un noyau de processeur unique, avec 1 Go de mémoire, il se bloque ou prend parfois 6-8 heures pour écrire la même quantité de données que une SQLBulkCopy qui prend quelques instants. Il gère mieux la mémoire cependant.

  while ((inputLine = rdr.ReadLine()) != null) //Read while the line is not null 
     { 
      Console.Write("\rWriting Line: {0}", k); 
      string[] arr;    
      arr = inputLine.Split('\t'); 

      /* items */ 
      if (fileName.Contains(",,")) 
      { 
       Item = Table(arr); 
       table.tables.InsertOnSubmit(Item); 

       /* Check to see if the item is in the db */ 
       bool exists = table.tables.Where(u => u.ProductID == Item.ProductID).Any(); 

       /* Commit */ 
       if (!exists) 
       { 
        try 
        { 
         table.SubmitChanges(); 
        } 
        catch (Exception e) 
        { 
         Console.WriteLine(e); 
         // Make some adjustments. 
         // ... 
         // Try again. 
         table.SubmitChanges(); 
        } 
       } 
      } 

Avec méthode d'assistance:

public static class extensionMethods 
{ 
    /// <summary> 
    /// Method that provides the T-SQL EXISTS call for any IQueryable (thus extending Linq). 
    /// </summary> 
    /// <remarks>Returns whether or not the predicate conditions exists at least one time.</remarks> 
    public static bool Exists<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate) 
    { 
     return source.Where(predicate).Any(); 
    } 
} 
+1

peut-être aussi l'élimination des rdr pourrait être une bonne idée –

+0

merci - je vais garder cela à l'esprit Je me demande, si je dispose mon lecteur avant Je finis le lecteur avant que je finisse de lire chaque fichier. Je perds mon rythme alors que je lis tout droit et que j'ai une erreur de mémoire alors que je ne lis pas pendant que j'écris ... sur la façon dont je voudrais revenir à l'endroit où j'étais dans le fichier? –

+0

s'il vous plaît voir ci-dessus commentaire ... –

Répondre

2

Essayez spécifiant la propriété BatchSize à 1000 qui sera charge l'insertion dans un lot 1000 record plutôt que l'ensemble du lot. Vous pouvez modifier cette valeur pour trouver ce qui est optimal. J'ai utilisé sqlbulkcopy pour des données de taille similaire et cela fonctionne bien.

+0

Merci, j'ai ajouté cela et en commençant à écrire tous les dossiers 100k, je pense que mon problème est avec l'objet lecteur de fichiers parce que je manque de mémoire à peu près au même endroit - essayer de comprendre comment je pourrais garder ma place dans le fichier que je lis tout droit en ce moment ... –

+0

Je ne pense toujours pas que le flux de fichiers est le problème. Avez-vous essayé la taille de traitement par lots d'environ 1000? Apparemment, le dosage à environ 1000-2000 enregistrements est le plus efficace. Si vous êtes certain que c'est le fichier, ce que vous pouvez faire est d'ouvrir le fichier, lire un millier d'enregistrements, stocker la position faire un encart en vrac, puis fermez le fichier. Ouvrez-le à nouveau et réglez la position au dernier pos et lisez un autre lot. –

+0

Vous devez avoir raison, car j'ai la copie réelle dans un bloc "try", et je reçois une exception de mémoire insuffisante, donc cela doit être lié à quelque chose dans le bloc try sinon je ne le traiterais pas correctement. Je tente d'enregistrer la position de mon fichier et de rapporter les résultats. –

1

Face au même problème, trouvé que le problème de OutOfMemory Exception était dans DataTable.Rows limites de quantité maximale. Résolu avec la table de recréation, avec une limite maximale de 500 000 lignes. Hope, ma solution sera helpfull:

var myTable = new System.Data.DataTable(); 
myTable.Columns.Add("Guid", typeof(Guid)); 
myTable.Columns.Add("Name", typeof(string)); 

int counter = 0; 

foreach (var row in rows) 
{ 
    ++counter; 

    if (counter < 500000) 
    { 
     myTable.Rows.Add(
      new object[] 
      { 
       row.Value.Guid, 
       row.Value.Name 
      }); 
    } 
    else 
    { 
     using (var dbConnection = new SqlConnection("Source=localhost;...")) 
     { 
      dbConnection.Open(); 
      using (var s = new SqlBulkCopy(dbConnection)) 
      { 
       s.DestinationTableName = "MyTable"; 

       foreach (var column in myTable.Columns) 
        s.ColumnMappings.Add(column.ToString(), column.ToString()); 

       try 
       { 
        s.WriteToServer(myTable); 
       } 
       catch (Exception ex) 
       { 
        Console.WriteLine(ex.Message); 
       } 
       finally 
       { 
        s.Close(); 
       } 
      } 
     } 

     myTable = new System.Data.DataTable(); 
     myTable.Columns.Add("Guid", typeof(Guid)); 
     myTable.Columns.Add("Name", typeof(string)); 

     myTable.Rows.Add(
      new object[] 
      { 
       row.Value.Guid, 
       row.Value.Name 
      }); 

     counter = 0; 

    } 
}