¿Cómo leo un archivo grande del disco a la base de datos sin quedarme sin memoria?

csv file-processing out-of-memory sqlbulkcopy sql-server-2005

Pregunta

Me siento avergonzado de hacer esta pregunta ya que siento que ya debería saber. Sin embargo, dado que no ... quiero saber cómo leer archivos grandes del disco a una base de datos sin obtener una excepción de OutOfMemory. Específicamente, necesito cargar CSV (o realmente archivos delimitados por tabulaciones).

Estoy experimentando con CSVReader y específicamente este ejemplo de código, pero estoy seguro de que lo estoy haciendo mal. Algunas de sus otras muestras de codificación muestran cómo se pueden leer archivos de cualquier tamaño, que es prácticamente lo que quiero (solo necesito leer desde el disco), pero no sé qué tipo de IDataReader podría crear para permitir esto. .

Estoy leyendo directamente desde el disco y mi intento de asegurarme de que nunca me quede sin memoria al leer demasiados datos a la vez es el siguiente. No puedo dejar de pensar que debería poder usar un BufferedFileReader o algo similar donde pueda apuntar a la ubicación del archivo y especificar un tamaño de búfer y luego CsvDataReader espera un IDataReader como su primer parámetro, podría usarlo. Muéstrame el error de mis formas, déjame deshacerme de mi método GetData con su mecanismo de fragmentación de archivos arbitrarios y ayúdame con este problema básico.

    private void button3_Click(object sender, EventArgs e)
    {   
        totalNumberOfLinesInFile = GetNumberOfRecordsInFile();
        totalNumberOfLinesProcessed = 0; 

        while (totalNumberOfLinesProcessed < totalNumberOfLinesInFile)
        {
            TextReader tr = GetData();
            using (CsvDataReader csvData = new CsvDataReader(tr, '\t'))
            {
                csvData.Settings.HasHeaders = false;
                csvData.Settings.SkipEmptyRecords = true;
                csvData.Settings.TrimWhitespace = true;

                for (int i = 0; i < 30; i++) // known number of columns for testing purposes
                {
                    csvData.Columns.Add("varchar");
                }

                using (SqlBulkCopy bulkCopy = new SqlBulkCopy(@"Data Source=XPDEVVM\XPDEV;Initial Catalog=MyTest;Integrated Security=SSPI;"))
                {
                    bulkCopy.DestinationTableName = "work.test";

                    for (int i = 0; i < 30; i++)
                    {
                        bulkCopy.ColumnMappings.Add(i, i); // map First to first_name
                    }

                    bulkCopy.WriteToServer(csvData);

                }
            }
        }
    }

    private TextReader GetData()
    {
        StringBuilder result = new StringBuilder();
        int totalDataLines = 0;
        using (FileStream fs = new FileStream(pathToFile, FileMode.Open, System.IO.FileAccess.Read, FileShare.ReadWrite))
        {
            using (StreamReader sr = new StreamReader(fs))
            {
                string line = string.Empty;
                while ((line = sr.ReadLine()) != null)
                {
                    if (line.StartsWith("D\t"))
                    {
                        totalDataLines++;
                        if (totalDataLines < 100000) // Arbitrary method of restricting how much data is read at once.
                        {
                            result.AppendLine(line);
                        }
                    }
                }
            }
        }
        totalNumberOfLinesProcessed += totalDataLines;
        return new StringReader(result.ToString());
    }

Respuesta aceptada

Probablemente no sea la respuesta que estás buscando, pero para eso fue diseñado BULK INSERT .


Respuesta popular

En realidad, su código lee todos los datos del archivo y lo guarda en TextReader (en la memoria). Luego lee los datos de TextReader para guardar el servidor.

Si los datos son tan grandes, el tamaño de los datos en TextReader produjo fuera de la memoria. Por favor, intente de esta manera.

1) Leer los datos (cada línea) del archivo.

2) Luego inserte cada línea en el servidor.

El problema de falta de memoria se resolverá porque solo se registra cada memoria en la memoria mientras se procesa.

Pseudo codigo

begin tran

While (data = FilerReader.ReadLine())
{
  insert into Table[col0,col1,etc] values (data[0], data[1], etc)
}

end tran


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué