我有一个包含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”类型异常
我该如何解决?或者还有另一种方式吗?
答案 0 :(得分:0)
答案 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()
}