[英]How to optimize the performance of comparisons for large data-sets?
我有一個非常慢的方法,我不確定如何優化它。 我也不太了解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循環之外的數據進行規范化。 我還嘗試使用字典來提高比較速度。 我現在不能做的是規范化數據庫並制作單個語句,而不是檢索整個表。 謝謝大家的幫助。 請告訴我代碼改進是否還有余地。
新代碼:
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;
}
}
第一個危險信號是您僅使用數據庫保存數據 。 如果允許的話 ,那頭野獸可以比您更快地搜索。
對於excel中的每一行,為數據庫建立一個相應的搜索語句並啟動它。 讓您的數據庫擔心搜索10K記錄的最佳方法。
第二個危險信號是未對現有數據進行規范化。 您想比較兩條街道,但是必須一遍又一遍地標准化它們。 為什么沒有一個稱為“ NormalizedStreet”的數據庫字段在插入時就已經應用了這些方法,而您可以在其中發起“相等”比較以規范化輸入數據?
總結一下:取消循環中的循環。 您剛剛重新創建了數據庫。 對於您的excel的每一行,構建一個(或兩個)語句以查找數據庫中是否存在該語句。 如果您想變得狡猾,請並行運行它們,但是我懷疑只需要700條輸入記錄就可以了。
首先,nvoight是正確的:您應該規范化數據庫中的數據並利用其功能進行搜索。 但是,如果您不能更改數據庫,則可以對代碼進行改進。
1)最重要的是要擺脫循環,只能完成一次。 這是數據規范化(替換,降低等)。 一次遍歷所有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)您可以使用字典或HashSets加快字符串搜索和比較的速度,而不是循環。
3)調用“ _crm”有多快? 也許外部通話會花費一些時間,這也是您運行緩慢的原因。
_crm.CheckContactExists(...)
如果這是sql server,則應該使用ssis。
它具有模糊匹配,這對於匹配來自兩個不同來源的地址上的記錄幾乎是必須的。
我也將使用ssis將數據導入到表中,並對管道中的數據進行任何預處理。
然后可以使用作業來運行整個過程。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.