我有一个非常慢的方法,我不知道如何优化它。我也不知道LINQ是如何工作的,所以如果解决方案是使用LINQ,请解释。非常感谢。
方法参数中的DataTable dtExcel
包含数据的第一部分,另一部分来自数据库的_dt
。
贯穿两个for循环的数据大致如下:700(dtExcel
)* 10,000(_dt
)= 7,000,000个比较。
这里是代码:
public async Task<DataTable> GetAdressesFromDB(DataTable dtExcel)
{
try
{
return await Task.Run(() =>
{
CurrentProgress = 0;
ProgressbarDBVisible = true;
_dtFoundDuplicates.Clear();
_dt = new DataTable();
_dt = DBConn.GetAllAddresses(dtExcel);
ProgressMaximum = dtExcel.Rows.Count;
for (int i = 0; i < dtExcel.Rows.Count; i++)
{
CurrentProgress++;
for (int y = 0; y < _dt.Rows.Count; y++)
{
// Criteria to check duplicates
string compareAdressExcel = "";
string compareAdressDB = "";
// Get the setted filter criteria and create both excel and db compare strings
string[] criteriaFields = ConfigurationManager.AppSettings["strFilter"].Split(',');
foreach (String cField in criteriaFields)
{
string cFieldTrimmed = cField.Trim();
if (cFieldTrimmed == "Strasse")
{
compareAdressExcel += dtExcel.Rows[i][cFieldTrimmed].ToString()
.ToLower()
.Replace(" ", "")
.Replace("str", "strasse")
.Replace("straße", "strasse")
.Replace("str.", "strasse");
compareAdressDB += _dt.Rows[y][cFieldTrimmed].ToString()
.Replace(" ", "")
.ToLower()
.Replace("str", "strasse")
.Replace("straße", "strasse")
.Replace("str.", "strasse");
}
else
{
compareAdressExcel += dtExcel.Rows[i][cFieldTrimmed].ToString().Replace(" ", "").ToLower();
compareAdressDB += _dt.Rows[y][cFieldTrimmed].ToString().Replace(" ", "").ToLower();
}
}
// If the company doesn't exists in Database, the contact to that company found in excel
// automatically won't exist either. Otherwise, check if contact exists.
if (compareAdressExcel == compareAdressDB)
{
string strOneExistTwoNot = "2";
if (!string.IsNullOrWhiteSpace(dtExcel.Rows[i]["FirstName"].ToString().Trim()) &&
!string.IsNullOrWhiteSpace(dtExcel.Rows[i]["LastName"].ToString().Trim()))
{
strOneExistTwoNot = _crm.CheckContactExists(Convert.ToInt32(_dt.Rows[y]["AdressNummer"].ToString().Trim()),
dtExcel.Rows[i]["FirstName"].ToString().Trim(),
dtExcel.Rows[i]["LastName"].ToString().Trim());
}
// Check if CheckContactExsists was successful
if (strOneExistTwoNot != "1" && strOneExistTwoNot != "2")
{
throw new Exception(strOneExistTwoNot);
}
// If Contact exists, mark row and add duplicate row,
// otherwise only add duplicate row
if (strOneExistTwoNot == "1")
{
dtExcel.Rows[i]["ContactExists"] = 1;
_dtFoundDuplicates.Rows.Add(dtExcel.Rows[i]["ID"], _dt.Rows[y]["AdressNummer"], "1");
}
else
{
_dtFoundDuplicates.Rows.Add(dtExcel.Rows[i]["ID"], _dt.Rows[y]["AdressNummer"], "0");
}
dtExcel.Rows[i]["AdressExists"] = 1;
}
}
}
ProgressbarDBVisible = false;
return dtExcel;
});
}
catch (Exception ex)
{
throw ex;
}
}
修改
好的,所以在@dlxeon
的回答的帮助下,我试图将我的数据标准化为我的两个for循环之外的数据。我还尝试使用Dictonary来提高比较速度。我现在不能做的是规范化数据库并制作单个语句而不是检索整个表。谢谢大家的帮助。请告诉我代码是否还有改进空间。
新代码:
public async Task<DataTable> GetAdressesFromDB(DataTable dtExcel)
{
try
{
return await Task.Run(() =>
{
CurrentProgress = 0;
ProgressbarDBVisible = true;
_dtFoundDuplicates.Clear();
_dt = DBConn.GetAllAddresses();
ProgressMaximum = dtExcel.Rows.Count;
// Normalization
string[] criteriaFields = ConfigurationManager.AppSettings["strFilter"].Split(',').Select(x => x.Trim()).ToArray();
Dictionary<int, string> excelAddresses = new Dictionary<int, string>();
for (int i = 0; i < dtExcel.Rows.Count; i++)
{
StringBuilder compareAdressExcel = new StringBuilder();
foreach (String cFieldTrimmed in criteriaFields)
{
if (cFieldTrimmed == "Strasse")
{
var normalizedValue = dtExcel.Rows[i][cFieldTrimmed].ToString()
.ToLower()
.Replace(" ", "")
.Replace("str", "strasse")
.Replace("straße", "strasse")
.Replace("str.", "strasse");
compareAdressExcel.Append(normalizedValue);
}
else
{
compareAdressExcel.Append(dtExcel.Rows[i][cFieldTrimmed].ToString().Replace(" ", "").ToLower());
}
}
excelAddresses.Add(i, compareAdressExcel.ToString());
}
Dictionary<int, string> dbAddresses = new Dictionary<int, string>();
for (int i = 0; i < _dt.Rows.Count; i++)
{
StringBuilder compareAdressDB = new StringBuilder();
foreach (String cFieldTrimmed in criteriaFields)
{
if (cFieldTrimmed == "Strasse")
{
var normalizedValue = _dt.Rows[i][cFieldTrimmed].ToString()
.ToLower()
.Replace(" ", "")
.Replace("str", "strasse")
.Replace("straße", "strasse")
.Replace("str.", "strasse");
compareAdressDB.Append(normalizedValue);
}
else
{
compareAdressDB.Append(_dt.Rows[i][cFieldTrimmed].ToString().Replace(" ", "").ToLower());
}
}
dbAddresses.Add(i, compareAdressDB.ToString());
}
foreach (var exAdd in excelAddresses)
{
CurrentProgress++;
foreach (var dbAdd in dbAddresses)
{
// If the company doesn't exists in Database, the contact to that company found in excel
// automatically won't exist either. Otherwise, check if contact exists.
if (exAdd.Value == dbAdd.Value)
{
string strOneExistTwoNot = "2";
if (!string.IsNullOrWhiteSpace(dtExcel.Rows[exAdd.Key]["FirstName"].ToString().Trim()) &&
!string.IsNullOrWhiteSpace(dtExcel.Rows[exAdd.Key]["LastName"].ToString().Trim()))
{
strOneExistTwoNot = _crm.CheckContactExists(Convert.ToInt32(_dt.Rows[dbAdd.Key]["AdressNummer"].ToString().Trim()),
dtExcel.Rows[exAdd.Key]["FirstName"].ToString().Trim(),
dtExcel.Rows[exAdd.Key]["LastName"].ToString().Trim());
}
// Check if CheckContactExsists was successful
if (strOneExistTwoNot != "1" && strOneExistTwoNot != "2")
{
throw new Exception(strOneExistTwoNot);
}
if (strOneExistTwoNot == "1")
{
dtExcel.Rows[exAdd.Key]["ContactExists"] = 1;
_dtFoundDuplicates.Rows.Add(dtExcel.Rows[exAdd.Key]["ID"], _dt.Rows[dbAdd.Key]["AdressNummer"], "1");
}
else
{
_dtFoundDuplicates.Rows.Add(dtExcel.Rows[exAdd.Key]["ID"], _dt.Rows[dbAdd.Key]["AdressNummer"], "0");
}
dtExcel.Rows[exAdd.Key]["AdressExists"] = 1;
}
}
}
ProgressbarDBVisible = false;
return dtExcel;
});
}
catch (Exception ex)
{
throw ex;
}
}
答案 0 :(得分:2)
第一个红色标志是您仅将数据库用于保存数据。如果你让它,那野兽可以比你更快地搜索。
对于excel中的每一行,为您的数据库构建一个相应的搜索语句并将其激活。让您的数据库担心搜索10K记录的最佳方式。
第二个红色标志是您的现有数据未进行标准化。你想要比较两条街道,但你必须将它们一遍又一遍地标准化。为什么没有名为“NormalizedStreet”的数据库字段已经 在插入时应用了一次,并且您可以在哪里触发“等于”比较来规范输入数据?
总结一下:废弃你的循环循环。你刚刚彻底改造了数据库。对于excel的每一行,构建一个或多个语句(以确定它是否存在于数据库中)。如果你想变得狡猾,可以并行运行它们,但是我怀疑你需要那些可用的700输入记录。
答案 1 :(得分:1)
首先,nvoight是对的:您应该规范化数据库中的数据并使用其权力进行搜索。但是,如果您无法更改数据库,则可以对代码进行改进。
1)最重要的是摆脱可以做一次的循环。这是数据规范化(替换,tolower等)。迭代所有Excel数据和数据库数据一次,以构建可以直接比较的数据,然后再对您的两个内部循环进行实际比较。您在循环中也不会更改您的配置,因此也可以将其移除。避免额外的字符串分配。您可以使用StringBuilder来构建字符串,而不是使用+ =
类似Excel的东西(然后是Db的类似循环)
string[] criteriaFields = ConfigurationManager.AppSettings["strFilter"].Split(',').Select(x => x.Trim()).ToArray();
List<string> excelAddresses = new List<string>();
for (int i = 0; i < dtExcel.Rows.Count; i++)
{
StringBuilder compareAdressExcel = new StringBuilder();
foreach (String cFieldTrimmed in criteriaFields)
{
if (cFieldTrimmed == "Strasse")
{
var normalizedValue = dtExcel.Rows[i][cFieldTrimmed].ToString()
.ToLower()
.Replace(" ", "")
.Replace("str", "strasse")
.Replace("straße", "strasse")
.Replace("str.", "strasse");
compareAdressExcel.Append(normalizedValue);
}
else
{
compareAdressExcel.Append(dtExcel.Rows[i][cFieldTrimmed].ToString().Replace(" ", "").ToLower());
}
}
excelAddresses.Add(compareAdressExcel.ToString());
}
然后你可以在主循环中使用标准化值
for (int i = 0; i < dtExcel.Rows.Count; i++)
{
CurrentProgress++;
for (int y = 0; y < _dt.Rows.Count; y++)
{
// Criteria to check duplicates
string compareAdressExcel = excelAddresses[i];
string compareAdressDB = dbAddresses[y];
2)您可以使用Dictionaries或HashSet来加速字符串搜索和比较,而不是循环。
3)对&#34; _crm&#34;的呼叫有多快?也许外部呼叫需要一段时间,这也是你速度缓慢的原因。
_crm.CheckContactExists(...)
答案 2 :(得分:0)
如果这是sql server,那么你应该使用ssis 它具有模糊匹配,这对于匹配来自两个不同来源的地址上的记录几乎是必须的 我也会使用ssis将数据导入表中,并对管道中的数据进行任何预处理 那么整个事情可以用一份工作来运行。