批量插入性能问题

Bulk Insert performance issues

提问人:user2661305 提问时间:10/31/2023 最后编辑:user2661305 更新时间:10/31/2023 访问量:82

问:

我有 160k 条记录需要处理,然后存储在 sql server 表中。我从临时表中检索它们,并执行一些验证和检查,以查找可能已存在的缺失字段或记录。

这在实体框架中非常慢,所以我修改了我的代码并希望使用 .我会查看我以前使用过的实体框架扩展,但我的工作不想承担使用它的许可费用。因为我需要对数据进行验证和其他检查,所以我需要将数据表传递给 ,而不是执行 .SqlBulkCopySqlBulkCopyIDataReader

这是我遇到问题的地方,将我的实体转换为具有 160k 行的数据表需要 20 多分钟。有没有人对如何改进其他建议有任何想法,这些建议可以快速使用如此大量的数据进行批量插入?IEnumerable

下面是我用来填充数据读取器的代码。我最初是在反射中完成这一切的,但删除了手动添加行,希望它会更快。

private static DataTable CreateRegisterDatatable(List<ApiRegisterData> apiRegisterData, List<Election> elections, IEnumerable<int> existingElectorIds, IEnumerable<int> missingElectionIds)
{
   var registerTable = new DataTable();

   foreach (var prop in typeof(Register).GetProperties())
   {
       DataColumn column = new DataColumn(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
       registerTable.Columns.Add(column);
   }

   foreach (var apiRegister in apiRegisterData)
   {
       if (!missingElectionIds.Contains(apiRegister.ElectionId) && !existingElectorIds.Contains(apiRegister.ElectorID))
       {
           DataRow row = registerTable.NewRow();
           row["ElectionID"] = elections[0].ElectionID;
           row["ElectorSuffix"] = apiRegister.ElectorSuffix.ToString();
           row["ElectorNumber"] = apiRegister.ElectorNumber;
           row["Title"] = string.IsNullOrEmpty(apiRegister.Title) ? DBNull.Value : apiRegister.Title;
           row["FirstName"] = string.IsNullOrEmpty(apiRegister.First_Name) ? DBNull.Value : apiRegister.First_Name;
           row["Initials"] = string.IsNullOrEmpty(apiRegister.Initials) ? DBNull.Value : apiRegister.Initials;
           row["Surname"] = string.IsNullOrEmpty(apiRegister.Surname) ? DBNull.Value : apiRegister.Surname;
           row["Suffix"] = string.IsNullOrEmpty(apiRegister.Suffix) ? DBNull.Value : apiRegister.Suffix;
           row["IneligibleReason"] = string.IsNullOrEmpty(apiRegister.IneligibleReason) ? DBNull.Value : apiRegister.IneligibleReason;
           row["Markers"] = string.IsNullOrEmpty(apiRegister.Markers) ? DBNull.Value : apiRegister.Markers;
           row["Nationality"] = string.IsNullOrEmpty(apiRegister.Nationality) ? DBNull.Value : apiRegister.Nationality;
           row["DOB"] = apiRegister.DOB is null ? DBNull.Value : apiRegister.DOB;
           row["DateOfAttainment"] = apiRegister.Date_Of_Attainment is null ? DBNull.Value : apiRegister.Date_Of_Attainment;
           row["OtherElector"] = Convert.ToBoolean(apiRegister.OtherElector);
           row["StreetName"] = string.IsNullOrEmpty(apiRegister.StreetName) ? DBNull.Value : apiRegister.StreetName;
           row["LocalityAddress1"] = string.IsNullOrEmpty(apiRegister.LocalityAddress1) ? DBNull.Value : apiRegister.LocalityAddress1;
           row["LocalityAddress2"] = string.IsNullOrEmpty(apiRegister.LocalityAddress2) ? DBNull.Value : apiRegister.LocalityAddress2;
           row["LocalityAddress3"] = string.IsNullOrEmpty(apiRegister.LocalityAddress3) ? DBNull.Value : apiRegister.LocalityAddress3;
           row["PostCode"] = string.IsNullOrEmpty(apiRegister.Postcode) ? DBNull.Value : apiRegister.Postcode;
           row["AbsentVoteAddress1"] = string.IsNullOrEmpty(apiRegister.ava1) ? DBNull.Value : apiRegister.ava1;
           row["AbsentVoteAddress2"] = string.IsNullOrEmpty(apiRegister.ava2) ? DBNull.Value : apiRegister.ava2;
           row["AbsentVoteAddress3"] = string.IsNullOrEmpty(apiRegister.ava3) ? DBNull.Value : apiRegister.ava3;
           row["AbsentVoteAddress4"] = string.IsNullOrEmpty(apiRegister.ava4) ? DBNull.Value : apiRegister.ava4;
           row["AbsentVoteAddress5"] = string.IsNullOrEmpty(apiRegister.ava5) ? DBNull.Value : apiRegister.ava5;
           row["AbsentVoteAddress6"] = string.IsNullOrEmpty(apiRegister.ava6) ? DBNull.Value : apiRegister.ava6;
           row["AbsentVotePostcode"] = string.IsNullOrEmpty(apiRegister.avap) ? DBNull.Value : apiRegister.avap;
           row["ProxyName"] = string.IsNullOrEmpty(apiRegister.ProxyName) ? DBNull.Value : apiRegister.ProxyName;
           row["CreatedDateTime"] = DateTime.UtcNow;
           row["UpdatedDateTime"] = DateTime.UtcNow;

           registerTable.Rows.Add(row);
        }
    }

    return registerTable;
}
C# .NET SQL 服务器

评论

1赞 T N 10/31/2023
不是一个完整的答案,但可以通过替换为在循环外计算的逻辑(我认为我是对的)然后在循环内使用来实现一些改进。row["Something"] = value;int somethingColumnIndex = registerTable.Columns["Something"].Ordinal;row[somethingColumnIndex] = value;
0赞 siggemannen 10/31/2023
不应该花这么长时间。但是你有 ,这些的 Contains 可能会很慢,具体取决于基础数据类型。现在使用 Sets 或 C# 中的任何名称IEnumerable<int> existingElectorIds, IEnumerable<int> missingElectionIds
0赞 Alex 10/31/2023
尝试简单的 ADO.NET,来自 SqlDataAdapter 的数据集 Fill 花了两秒钟才能在我的机器上获得 1M 行。learn.microsoft.com/en-us/dotnet/framework/data/adonet/......
3赞 Stu 10/31/2023
如果性能是你的主要标准,那么就放弃将数据拖出数据库,尤其是使用 orm,将数据库用于它擅长的事情,处理数据。
3赞 Zohar Peled 10/31/2023
我同意@Stu - 我在您的代码上看不到无法在 T-SQL 中轻松应用的逻辑,所以我真的看不到在这里使用 c# 的任何好处。

答: 暂无答案