-1

我有一个包含 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”类型的未处理异常

我该如何解决?还是有其他方法?

4

3 回答 3

0

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

批量插入 CSV

问候。

于 2013-07-23T09:47:30.893 回答
0

取自 MSDN:

关于 .ReadLine()

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

于 2013-07-23T09:49:44.883 回答
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()
}
于 2013-07-23T10:03:13.690 回答