【发布时间】:2021-06-18 01:02:44
【问题描述】:
已将以下代码放在一起以读取一组特定的 CSV 文件。它有效,但在很大程度上是一项正在进行的工作。有一段代码(填充数据表行 - 请参见下面的片段)与 SqlBulkCopy 操作的运行时间一样长。就如何提高性能征求意见/建议。
在代码(如下)中,以 50K 批次处理约 15M 行文件只用了不到 11.5 分钟。分解部分。 SqlBulkCopy 耗时约 236Kms(4 分钟),阅读器仅需 105Kms(约 1.5 分钟),而填充数据表的部分耗时约 200Kms(3.33 分钟)。
csvTableTimer.Start();
// Process row and populate datatable
DataRow dr = dt.NewRow();
foreach (DataColumn dc in dt.Columns)
{
if (row.GetType().GetProperty(dc.ToString()).GetValue(row) != null)
{
dr[dc.ToString()] = row.GetType().GetProperty(dc.ToString()).GetValue(row);
}
}
dt.Rows.Add(dr);
csvTableTimer.Stop();
CSV 文件非常大(10+GB)并且没有标题。我正在使用 Class 来构建数据表结构,并且喜欢在填充数据表行时继续使用这种方法,因为我需要扩展它以使用多种 CSV 类型。
数据表反映了与 SQL DB 表对齐的类中的列名。曾想使用 GetField(已转换,不是原始)遍历数据表 row[column.ColumnName] = csv.GetField( column.DataType, column.ColumnName ); 中的每一列,但一直收到关于没有标题的错误。发现了一个与 HasHeaderRecord = false 相关的未解决问题,这与我试图做的事情相匹配,这增加了我向那些更熟练的人寻求建议的愿望。感谢您的帮助!
在代码块上展开;
var rconfig = new CsvHelper.Configuration.CsvConfiguration(CultureInfo.InvariantCulture)
{
BufferSize = 1024,
Delimiter = ",",
AllowComments = true,
HasHeaderRecord = false,
HeaderValidated = null,
IgnoreBlankLines = true,
MissingFieldFound = null,
Comment = '#',
Escape = '"',
TrimOptions = TrimOptions.Trim,
BadDataFound = x =>
{
isBadRecord = true;
ErrRecords.Add(x.RawRecord);
++badCount;
}
};
var loadFType = @"B";
// Create datatable using class as definition.
PropertyDescriptorCollection props1 = TypeDescriptor.GetProperties(loaderFileType);
DataTable dt = new DataTable();
dt = UtilExtensions.CreateDataTable(props1);
using (var reader = new StreamReader(rFile))
{
reader.ReadLine();
using (var csv = new CsvReader(reader, rconfig))
{
switch (loadFType)
{
case "ALL":
csv.Context.RegisterClassMap<CSVLoader.AMap>();
var allRecords = new List<CSVLoader.A>();
break;
case "BAL":
csv.Context.RegisterClassMap<CSVLoader.BMap>();
var balRecords = new List<CSVLoader.B>();
break;
case "CIF":
csv.Context.RegisterClassMap<CSVLoader.CMap>();
var cifRecords = new List<CSVLoader.C>();
break;
}
dt.BeginLoadData();
while (csv.Read())
{
csvReadTimer.Start();
var row = csv.GetRecord(loaderFileType);
csvReadTimer.Stop();
runningCount++;
if (!isBadRecord)
{
csvTableTimer.Start();
// Process row and populate datatable
DataRow dr = dt.NewRow();
foreach (DataColumn dc in dt.Columns)
{
if (row.GetType().GetProperty(dc.ToString()).GetValue(row) != null)
{
dr[dc.ToString()] = row.GetType().GetProperty(dc.ToString()).GetValue(row);
}
}
dt.Rows.Add(dr);
csvTableTimer.Stop();
++goodCount;
if (batchCount >= dtbatchSize || runningCount >= fileRecCount)
{
try
{
// Write from the source to the destination.
bcpLoadTimer.Start();
bulkCopy.WriteToServer(dt);
bcpLoadTimer.Stop();
bcpLoadBatchCount++;
}
catch (Exception ex)
{
}
dt.Clear();
batchCount = 0;
}
batchCount++;
}
isBadRecord = false;
}
dt.EndLoadData();
reader.Close();
dt.Clear();
transaction.Commit();
// B
public class B
{
[Index(0)]
public string A { get; set; }
[Index(1)]
public string BString { get; set; }
[Index(2)]
public int? C { get; set; }
[Index(3)]
public string D { get; set; }
[Index(4)]
public string E { get; set; }
[Index(5)]
public DateTime? F { get; set; }
[Index(6)]
public decimal? G { get; set; }
[Index(7)]
public decimal? H { get; set; }
[Index(8)]
public decimal? I { get; set; }
[Index(9)]
public decimal? J { get; set; }
[Index(10)]
public int? K { get; set; }
[Index(11)]
public string L { get; set; }
[Index(12)]
public DateTime? M { get; set; }
}
// B
public sealed class BMap : ClassMap<B>
{
public BMap()
{
// AutoMap(CultureInfo.InvariantCulture);
Map(m => m.A).Index(0);
Map(m => m.BString).Index(1);
Map(m => m.C).Index(2);
Map(m => m.D).Index(3);
Map(m => m.E).Index(4);
Map(m => m.F).Index(5).TypeConverterOption.Format("yyyyMMdd");
Map(m => m.G).Index(6);
Map(m => m.H).Index(7);
Map(m => m.I).Index(8);
Map(m => m.J).Index(9);
Map(m => m.K).Index(10);
Map(m => m.L).Index(11);
Map(m => m.M).Index(12).TypeConverterOption.Format("yyyy-MM-dd-hh.mm.ss.ffffff");
}
}
【问题讨论】:
-
我不认为你可以分享minimal reproducible example 可以吗?我尝试将您的代码发布到小提琴中并得到了几十个编译错误,请参阅dotnetfiddle.net/LLcWtw。
-
抱歉,我试图不让发布时间过长。将发布完整的运行块。
-
minimal reproducible example 不仅仅是整个代码块的剪切和粘贴,它需要最少 数量的代码重现问题。例如,我尝试尽可能简化
csvTableTimer的计算,请参见此处:dotnetfiddle.net/UsUAgc。你认为那是minimal reproducible example吗?
标签: c# performance datatable csvhelper