問題描述
我有兩個資料表:
-
dt:從 CSV 檔案填充超過 170 萬行 -
dataStructure.Tables["AccountData"]:從資料庫查詢也是大約一百萬行
我使用以下程式碼遍歷並比較每組行的資料。程式碼需要 48 小時才能完成。我將應用程式的屬性更改為 x64,以允許它使用更多的 Process Memory 。它現在使用大約 2.5GB 。
我的問題是,有沒有更有效的方法來減少執行時間?
//set is_legal column value for each row
foreach (DataRow row in dt.Rows)
{
var acctNum = row[0].ToString().Replace(""", "");
foreach (DataRow queryRow in dataStructure.Tables["AccountData"].Rows)
{
var queryAcctNum = queryRow[0].ToString();
if (acctNum.Equals(queryAcctNum))
{
row[12] = "Y";
Console.WriteLine("Yes count: " + cnt);
}
else
{
row[12] = "N";
}
}
cnt++;
};
如何填充 dataStructure.Tables["AccountData"]:
//Read each row from the table and output the results into the data set
while (readFile.Read())
{
//Create a row to hold data
DataRow datarow = dataStructure.Tables["AccountData"].NewRow();
datarow["AccountNumber"] = readFile.GetString(0).Trim();
datarow["LegalStatus"] = readFile.GetString(1);
//add the row to the data table AccountData
dataStructure.Tables["AccountData"].Rows.Add(datarow);
}
最佳解決思路
你的內部迴圈似乎是不必要的。為什麼不建立查詢:
var knownAccountNumbers = new HashSet<string>(
dataStructure.Tables["AccountData"].Rows
.Cast<DataRow>()
.Select(row => row[0].ToString()));
現在你的迴圈是簡單的:
foreach (DataRow row in dt.Rows)
{
var accountNumber = row[0].ToString().Replace(""", "");
row[12] = knownAccountNumbers.Contains(accountNumber) ? "Y" : "N";
}
我想我記得讀一次,HashSet 的記憶體使用量是每個條目 12 個位元組,條目大小。所以你看的是 12MB + 1,000,000 *(2 * accountNumber.Length) 。所以基本上沒有什麼事情的宏偉計劃。然而,您正在獲得不斷的時間查詢,這對於這種工作應該是一個巨大的好處。
命名時應該更加小心。不要縮寫例如 acctNum – > accountNumber 。
次佳解決思路
該程式碼似乎被破壞,您找不到匹配後不會 break;,所以所有記錄都可能有 row[12] == "N"。
你應該真正在 accountNumber 上加入:
var matchingRows =
from DataRow row in dt.Rows
let rowKey = row[0].ToString().Replace(""", "")
join DataRow queryRow in dataStructure.Tables["AccountData"].Rows
on rowKey equals queryRow[0]
select row;
foreach(var row in matchingRows)
{
row[12] = "Y";
}
這樣,您將停止在第一場比賽中進行搜尋,並只更新匹配的行。
第三種解決思路
幾點評論:
避免記憶體密集型資料型別
而不是使用 DataTable for dt,只需從 csv 直接讀取一行一行 (readLine(),然後拆分 (‘,’),這將大大減少記憶體使用量,而不是一次載入所有 170 萬行,當你一次只使用一個。
排序的資料更快
按帳號對 dataStructure 進行排序。之後,您可以進行二進位制搜尋以查詢帳號,並在您重複它們之後進行休息。這將大大減少你的迴圈時間。如果您可以在將資料讀入 dataStructure 之前獲取資料庫進行排序,更好。
替代想法
您也可以嘗試將所有 dt 載入到 dataStructure 執行的同一資料庫中的臨時表中,然後使用儲存過程進行更新。資料庫將能夠比 C#中的迴圈更有效地執行此更新
第四種思路
知道您正在迭代 dataStructure.Tables["AccountData"]的所有記錄,無論您是否找到匹配項。你真的應該 break 出這個迴圈,如果 acctNum.Equals(queryAcctNum)。這應該加快你的任務很多 (至少如果可以找到資料) 。
另一個可能的增強可能是對錶的行進行排序,並儲存內迴圈的最後找到的”index” 以將其用作下一次迭代的開始。這將需要將迴圈從 foreach 更改為 could be faster anyway 的正常 for 迴圈。
假設 dt 的第一列也被命名為”AccountNumber”,這應該加快程式
dt.DefaultView.Sort = "AccountNumber";
var accountDataTable = dataStructure.Tables["AccountData"];
accountDataTable.DefaultView.Sort = "AccountNumber";
int numberOfAccountDataRows = accountDataTable.Rows.Count;
int currentIndex = 0;
foreach (DataRow row in dt.Rows)
{
var acctNum = row[0].ToString().Replace(""", "");
for(int i = currentIndex; i < numberOfAccountDataRows; i++)
{
var queryAcctNum = accountDataTable.Rows[i][0].ToString();
if (acctNum.Equals(queryAcctNum))
{
row[12] = "Y";
currentIndex = i;
break;
}
}
}
第五種思路
免責宣告:我不在 C#中編碼,所以你可能會失望。如果有人願意將其移植到 C#,請做,這可能會幫助 OP 更多。
比較兩個 data-sets 進行包含/排除可以在流上進行,只要它們被排序。
該演演算法接近合併排序的合併透過; 在 pseudo-code
left = /*sorted stream 1*/
right = /*sorted stream 2*/
while not left.empty() and not right.empty():
if left.current() == right.current():
print "Common item", left.current()
left.moveToNext()
right.moveToNext()
continue
if left.current() < right.current():
print "Left specific item", left.current()
left.moveToNext()
continue
if left.current() > right.current():
print "Right specific item", right.current()
right.moveToNext()
continue
while not left.empty()
print "Left specific item", left.current()
left.moveToNext()
while not right.empty()
print "Right specific item", right.current()
right.moveToNext()
優點:
-
恆定 memory
-
快速
缺點:
-
需要預先排列兩個流
如果您可以輕鬆地按排序順序獲取這兩個流,那麼它可能會獲勝。
如果您必須準備資料集 (排序),那麼對於非常大的資料集 (幾乎不適合記憶體) 仍然是有利的。
如果兩個資料集都很容易適合記憶體,而且一個沒有排序,那麼使用另一個解決方案 (在 hash-map 和 look-up 中將較小的一個解壓縮到其中,同時遍歷較大的一個) 。
參考文獻
注:本文內容整合自 Google/Baidu/Bing 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。