由于内存问题,我决定将DataTable替换/转换为IDataReader。
经过相当长一段时间的谷歌&MSDN搜索,我在http://www.michaelbowersox.com/2011/12/22/using-a-custom-idatareader-to-stream-data-into-a-database/和大容量插入Sql Server数百万条记录。
由于我使用的是LumenWorks快速CSV阅读器,我还不知道如何告诉CsvReader让IDataReader使用两个不同的字段版本-(csvReader.FieldCount是这里的关键,但我不知道如何告诉csvReader使用具有IDataReader接口的两个新类中的任何一个。请参阅下面的原始脚本和修改后的脚本…谢谢…
//原始脚本。。。
var dbConnection = new SqlConnection(_dbConnectionString);
using (var dbBulkCopy = new SqlBulkCopy(dbConnection)
{
using (CsvReader csvReader = new CsvReader(new StreamReader(filePath), false, '|', '"', '\', '#', ValueTrimmingOptions.UnquoteOnly))
{
while(csvReader.ReadNextRecord())
{
if (csvReader.FieldCount == 48)
{
//Version 1...
dataRow["DealerId"] = csvReader[0];
dataRow["DealerName"] = csvReader[1];
//Etc...
}
else if (csvReader.FieldCount == 51)
{
//Version 2...
dataRow["DealerId"] = csvReader[0];
dataRow["DealerName"] = csvReader[1];
//Etc...
}
else { throw new Exception("Field Total Count Mismatched"); }
dataTable.Rows.Add(dataRow);
}
dbConnection.Open();
dbBulkCopy.WriteToServer(dataTable);
}
}
//新建脚本。。。
var dbConnection = new SqlConnection(_dbConnectionString);
using (var dbBulkCopy = new SqlBulkCopy(dbConnection)
{
dbConnection.Open();
using (CsvReader csvReader = new CsvReader(new StreamReader(filePath), false, '|', '"', '\', '#', ValueTrimmingOptions.UnquoteOnly))
{
csvReader.ReadNextRecord();
dbBulkCopy.WriteToServer(
if (csvReader.FieldCount == 48)
{
//Version 1...
csvReader....??? //Assign a custom class having IDataTable...
}
else if (csvReader.FieldCount == 51)
{
//Version 2...
csvReader....??? //Assign a custom class having IDataTable...
}
else { throw new Exception("Field Total Count Mismatched"); }
);
}
}
//示例脚本。。。
using (var file = new StreamReader(path))
using (var csv = new CsvReader(file, true)) // true = has header row
using (var bcp = new SqlBulkCopy(connection)) {
bcp.DestinationTableName = "TableName";
bcp.WriteToServer(csv);
}
因为它会有点长,所以我把它作为一个答案来写。
*我假设,尽管您有两种带有不同字段顺序的csv文件,但目标表是相同的*[EDIT]无需假设,您在评论中指出。
为了能够理解您的上下文,我从这里获得了一些示例数据。
假设第一种类型的文件如下所示:
Rk,Year,Age,Tm,Lg,Id,G,W,L,W-L,Finish,
1,1978,37,Atlanta Braves,NL,,162,69,93,.426,6
第二种类型是(有些列的Age<->Finish相反,还有其他字段)
Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3
1,1978,Very good year,Atlanta Braves,NL,,162,69,93,.426,96,,,,
因此,目标表看起来像(只有列)
Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3
我在这里看到两个选项(末尾+1个选项):
选项1
- 添加步骤0,通过定义字段格式,使所有输入文件在字段级别上统一。这可以通过创建与数据库中相同的字段来完成
[假设Test4和Test5是目标表上存在但在两个CSV文件中都缺少的列]
Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3,Test4,Test5
根据您定义的格式,分析您拥有的所有文件(两种类型),将它们重写为一个(或多个,由您决定)。这样,您只有一个(或多个)具有唯一格式的文件。
您现在可以使用csv阅读器解析此文件,将其插入数据库,因为字段不兼容问题是用您以唯一格式获得的最后一个文件处理的。
选项2
您将执行两次SqlBulkCopy操作。第一轮你将读取具有48个字段的文件,下一轮你将阅读具有51个字段的文档。
var FilesWith48Fields = Directory.GetFiles(@"D:Data48Fields", "*.csv");
foreach (var fileName in FilesWith48Fields)
{
using (var file = new StreamReader(fileName))
using (var csv = new CsvReader(file, true)) // true = has header row
using (var bcp = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepNulls))
{
bcp.DestinationTableName = "fletchsodTable";
// map the first field of your CSV to the column in Database
var mapping1 = new SqlBulkCopyColumnMapping(0, "FirstColumnName");
bcp.ColumnMappings.Add(mapping1);
var mapping2 = new SqlBulkCopyColumnMapping(1, "SecondColumnName");
bcp.ColumnMappings.Add(mapping2);
....
bcp.WriteToServer(csv);
}
}
并对具有51个字段的文件重复相同操作
var FilesWith51Fields = Directory.GetFiles(@"D:Data51Fields", "*.csv");
......
有关SqlBulkCopyColumnMapping的详细信息,请单击此处。
选项3
如果你想冒险创建你的数据阅读器,这里有一些链接:
Daniel Wertheim的博客
代码项目的实例实现
另一个
最后是MSDN
个人笔记由于时间不够,对于我的一个类似问题,我放弃了第三个选项,因为有了所有的单元测试和优化,你必须做另一个调整,这可能需要时间(至少对我来说,情况是)
选项4也许,对于我在OPTION 2中指出的列映射,您可能希望通过测试字段计数来实现您的方法。但出于本能,我建议不要用硬编码整数来计算字段。