sqlbulkcopy mem. la gestion

datatable memory sqlbulkcopy

Question

J'utilise SQLBULKCOPY pour copier certaines tables de données dans une table de base de données. Toutefois, étant donné que la taille des fichiers que je copie est parfois supérieure à 600 Mo, la mémoire manque constamment.

J'espère obtenir des conseils sur la gestion de la taille de la table avant de l'enregistrer dans la base de données afin de libérer de la mémoire pour continuer à écrire.

Voici quelques exemples de mon code (certaines colonnes et lignes ont été éliminées pour plus de 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());

    }

J'ai continué à avoir des problèmes pour faire fonctionner SQLBulkCopy, et j'ai réalisé que je devais faire plus de travail sur chaque enregistrement avant qu'il ne soit entré dans la base de données. J'ai donc développé une méthode simple LinQ to Sql pour effectuer des mises à jour enregistrement par enregistrement, afin de pouvoir modifier d'autres informations et créer plus d'informations d'enregistrement au fur et à mesure de son exécution,

Problème: cette méthode fonctionne assez lentement (même sur les machines Core i3). Vous avez des idées pour l’accélérer (threading?) - sur un seul cœur de processeur, avec 1 Go de mémoire, il se bloque ou prend parfois de 6 à 8 heures à écrire la même quantité de données qu'un SQLBulkCopy qui prend quelques instants. Cependant, il gère mieux la mémoire.

            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();
    }
}

Réponse acceptée

Essayez de spécifier la propriété BatchSize sur 1000, ce qui permettra de regrouper l'insertion dans un lot de 1000 enregistrements plutôt que dans son intégralité. 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.


Réponse populaire

Face au même problème, a constaté que le problème de l'exception OutOfMemory était lié aux limitations de quantité maximales dans DataTable.Rows. Résolu avec la table de recréation, avec une limite maximale de 500 000 lignes. J'espère que ma solution sera utile:

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;

    }
}


Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi