sqlbulkcopy mem. administración

datatable memory sqlbulkcopy

Pregunta

Estoy usando SQLBULKCOPY para copiar algunas tablas de datos en una tabla de base de datos, sin embargo, debido a que el tamaño de los archivos que estoy copiando se ejecuta a veces por encima de 600 mb, me estoy quedando sin memoria.

Espero obtener algunos consejos sobre la administración del tamaño de la tabla antes de ingresar a la base de datos para poder liberar algo de memoria y seguir escribiendo.

Aquí hay algunos ejemplos de mi código (algunas columnas y filas eliminadas por simplicidad)

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

    }

Seguí teniendo problemas para hacer funcionar SQLBulkCopy, y me di cuenta de que necesitaba hacer más trabajo en cada registro antes de ingresar a la base de datos, así que desarrollé un método simple de LinQ a Sql para hacer las actualizaciones de registro por registro, para poder editar otra información y crear más información de registro mientras se estaba ejecutando,

Problema: este método se está ejecutando bastante lento (incluso en la máquina Core i3), alguna idea sobre cómo acelerarlo (¿enhebrar?) - en un solo núcleo de procesador, con 1 gb de memoria se bloquea o toma a veces 6-8 horas para escriba la misma cantidad de datos que una copia de SQLBulk que toma unos momentos. Aunque gestiona la memoria mejor.

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

Con el método de ayuda:

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

Respuesta aceptada

Intente especificar la propiedad BatchSize a 1000, que hará un lote del inserto en un lote de 1000 registros en lugar de todo el lote. Puede ajustar este valor para encontrar lo que es óptimo. He usado sqlbulkcopy para datos de tamaño similar y funciona bien.


Respuesta popular

Ante el mismo problema, se encontró que el problema de la excepción OutOfMemory estaba en DataTable.Rows limitaciones de cantidad máxima. Resuelto con la tabla de recreación, con un límite máximo de 500000 filas. Espero que mi solución te sea de utilidad.

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;

    }
}


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow