将巨大的CSV文件导出到数据库c#

时间:2013-07-23 09:32:42

标签: c# database csv

我有一个包含1000万个条目的大型CSV文件,我需要使用C#将其导出到SQL。我是新手,我真的不知道怎么写这个。 到目前为止我有这样的事情:

private static void ExportToDB()
        {
             SqlConnection con = new SqlConnection(@"Data Source=SHAWHP\SQLEXPRESS;Initial Catalog=FOO;Persist Security Info=True;User ID=sa");
             string filepath = @"E:\Temp.csv";
            StreamReader sr = new StreamReader(filepath);
            string line = sr.ReadLine();
            string[] value = line.Split(',');
            DataTable dt = new DataTable();
            DataRow row;
            foreach (string dc in value)
            {
                dt.Columns.Add(new DataColumn(dc));
            }

            while ( !sr.EndOfStream )
            {
                value = sr.ReadLine().Split(',');
                if(value.Length == dt.Columns.Count)
                {
                    row = dt.NewRow();
                    row.ItemArray = value;
                    dt.Rows.Add(row);
                }
            }
            SqlBulkCopy bc = new SqlBulkCopy(con.ConnectionString, SqlBulkCopyOptions.TableLock);
            bc.DestinationTableName = "tblparam_test";
            bc.BatchSize = dt.Rows.Count;
            con.Open();
            bc.WriteToServer(dt);
            bc.Close();
            con.Close();
        }

。 它给了我一个错误,说: mscorlib.dll中出现未处理的“System.OutOfMemoryException”类型异常

我该如何解决?或者还有另一种方式吗?

3 个答案:

答案 0 :(得分:0)

如果您可以将文件发送到服务器。我会使用批量插入服务器端。

BULK Insert CSV

问候。

答案 1 :(得分:0)

取自MSDN:

与.ReadLine()

有关

如果当前方法抛出OutOfMemoryException,则读取器在底层Stream对象中的位置按方法能够读取的字符数提前,但已经读入内部ReadLine缓冲区的字符将被丢弃。如果在将数据读入缓冲区后操纵基础流的位置,则基础流的位置可能与内部缓冲区的位置不匹配。要重置内部缓冲区,请调用 DiscardBufferedData 方法;但是,这种方法会降低性能,只有在绝对必要时才应该调用。

答案 2 :(得分:0)

你不能使用这种方法,因为string.Split会创建大量可以增加内存量的数组。假设你有10列。分割后,您将拥有10个数组长度和10个字符串= 11个对象。它们每个都有8或16字节的额外内存(对象同步根等)。因此,每个字符串的内存开销为88字节。 10 KK行将消耗至少880KK内存 - 并添加到您的文件的此数字大小,您将具有1GB的值。这不是全部,DateRow是相当重的结构,所以,你应该添加10KK的数据行。并非全部 - 大小为10KK的DataTable将具有超过40mb的大小。 因此,预期所需的大小超过1Gb。

对于х32进程.Net不能轻易使用超过1Gb的内存。理论上它有2场演出,但这只是理论上,因为一切都消耗内存 - 程序集,本机dll和其他对象,UI等。

解决方案是使用х64进程或如下所示的块读写

    private static void ExportToDB()
    {
         string filepath = @"E:\Temp.csv";
        StreamReader sr = new StreamReader(filepath);
        string line = sr.ReadLine();
        string[] value = line.Split(',');
        DataTable dt = new DataTable();
        DataRow row;
        foreach (string dc in value)
        {
            dt.Columns.Add(new DataColumn(dc));
        }

        int i = 1000; // chunk size
        while ( !sr.EndOfStream )
        {
            i--
            value = sr.ReadLine().Split(',');
            if(value.Length == dt.Columns.Count)
            {
                row = dt.NewRow();
                row.ItemArray = value;
                dt.Rows.Add(row);
            }
            if(i > 0)
               continue;
            WriteChunk(dt);                 
            i = 1000;
        }
        WriteChunk(dt);

    }
void WriteChunk(DataTable dt)
{
         SqlConnection con = new SqlConnection(@"Data Source=SHAWHP\SQLEXPRESS;Initial Catalog=FOO;Persist Security Info=True;User ID=sa");
    using(SqlBulkCopy bc = new SqlBulkCopy(con.ConnectionString, SqlBulkCopyOptions.TableLock))
    {
        bc.DestinationTableName = "tblparam_test";
        bc.BatchSize = dt.Rows.Count;
        using(con.Open())
        {
            bc.WriteToServer(dt);
        }
    }
    dt.Rows.Clear()
}