我正在处理我的应用程序中的一些功能,它们查询我们的数据库并将数据拉到一个数据表,然后打开一个excel文件并填充另一个数据表。
因为excel文件不包含可用的ID,所以我无法对数据进行排序,也可能无法使用DataTable.Merge()
。
以下是我创建的匹配算法的代码。
private void RunMatchingAlgorithm()
{
// Initialize variables
string partNumber = "";
DateTime expiration_date = DateTime.Now;
decimal contract_cost = 0;
string contract_no = "";
string partNumber2 = "";
DateTime expiration_date2 = DateTime.Now;
decimal contract_cost2 = 0;
string contract_no2 = "";
//Get values from DataBase
for (int i = 0; i < dtFromTableContracts.Rows.Count; i++)
{
partNumber2 = dtFromTableContracts.Rows[i]["supplier_part_no"].ToString();
contract_no2 = dtFromTableContracts.Rows[i]["contract_no"].
expiration_date2 = Convert.ToDateTime(dtFromTableContracts.Rows[i]["con_end_date"]).Date;
//Get Values from converted Excel table
for (int j = 0; j < dtConversion.Rows.Count; j++)
{
contract_no = dtConversion.Rows[j]["vend_contract_no"].ToString();
//If we have even a partial match, check for a part number match
if (contract_no2.StartsWith(contract_no))
{
partNumber = dtConversion.Rows[j]["vend_item_id"].ToString();
//If the values match, populate from both tables
if (partNumber == partNumber2)
{
dtConversion.Rows[j]["wpd_expiration_date"] = expiration_date2.Date;
dtConversion.Rows[j]["wpd_cont_cost"] = dtFromTableContracts.Rows[i]["contract_cost"];
dtConversion.Rows[j]["wpd_contract_no"] = dtFromTableContracts.Rows[i]["contract_no"];
dtConversion.Rows[j]["wpd_item_id"] = dtFromTableContracts.Rows[i]["supplier_part_no"];
dtConversion.Rows[j]["wpd_item_no"] = dtFromTableContracts.Rows[i]["item_id"];
dtConversion.Rows[j]["discontinued"] = dtFromTableContracts.Rows[i]["discontinued"];
dtConversion.Rows[j]["job_no"] = dtFromTableContracts.Rows[i]["job_no"];
}
}
}
}
}
如果您感到好奇,以后的方法会删除所有不匹配的行,我们只会在DGV中显示匹配的记录。
这当前按预期工作,但如果我的Big O符号是正确的,我正在处理O(m * n),这对于更大的数据集而言变得非常慢,而且处理器密集程度极高。
我正在寻找一种更有效的方法来实现这一点,而不是循环每一行,因为我们使用的一些excel电子表格接近40,000行。使用该集合的大小,此算法大约需要6分钟。
答案 0 :(得分:3)
哦,小伙子,代码有很多简化的机会。您可以减少局部变量的范围,消除任何诱惑,为它们分配未使用的值。除了访问集合外,还可以在不使用索引时将For循环转换为ForEach循环。
初步简化:
private void RunMatchingAlgorithm() {
foreach (var databaseRow in dtFromTableContracts.Rows) {
string partNumber2 = databaseRow["supplier_part_no"].ToString();
string contract_no2 = databaseRow["contract_no"].ToString();
DateTime expiration_date2 = Convert.ToDateTime(databaseRow["con_end_date"]).Date;
foreach (var excelRow in dtConversion.Rows) {
string contract_no = excelRow["vend_contract_no"].ToString();
//If we have even a partial match, check for a part number match
if (contract_no2.StartsWith(contract_no)) {
string partNumber = excelRow["vend_item_id"].ToString();
//If the values match, populate from both tables
if (partNumber == partNumber2) {
excelRow["wpd_expiration_date"] = expiration_date2.Date;
excelRow["wpd_cont_cost"] = databaseRow["contract_cost"];
excelRow["wpd_contract_no"] = databaseRow["contract_no"];
excelRow["wpd_item_id"] = databaseRow["supplier_part_no"];
excelRow["wpd_item_no"] = databaseRow["item_id"];
excelRow["discontinued"] = databaseRow["discontinued"];
excelRow["job_no"] = databaseRow["job_no"];
}
}
}
}
}
想想看,这几乎是linq查询的设计用例。我们可以将大部分代码转换为查询:
private void RunMatchingAlgorithm() {
var matches = from databaseRow in dtFromTableContracts.Rows
let partNumber2 = databaseRow["supplier_part_no"].ToString()
let contract_no2 = databaseRow["contract_no"].ToString()
let expiration_date2 = Convert.ToDateTime(databaseRow["con_end_date"]).Date
from excelRow in dtConversion.Rows
let contract_no = excelRow["vend_contract_no"].ToString()
where contract_no2.StartsWith(contract_no)
let partNumber = excelRow["vend_item_id"].ToString()
where partNumber == partNumber2
select new { databaseRow, excelRow, expiration_date2 }
foreach (var m in matches) {
var dst = m.excelRow;
var src = m.databaseRow;
dst["wpd_expiration_date"] = m.expiration_date2.Date;
dst["wpd_cont_cost"] = src["contract_cost"];
dst["wpd_contract_no"] = src["contract_no"];
dst["wpd_item_id"] = src["supplier_part_no"];
dst["wpd_item_no"] = src["item_id"];
dst["discontinued"] = src["discontinued"];
dst["job_no"] = src["job_no"];
}
}
现在我看到可以应用优化的位置。我们正在使用'where'进行嵌套'from',这相当于交叉连接。此外,我们可以削减大部分现在只使用过的临时演员:
private void RunMatchingAlgorithm() {
var matches = from databaseRow in dtFromTableContracts.Rows
join excelRow in dtConversion.Rows
on excelRow["vend_item_id"].ToString() equals databaseRow["supplier_part_no"].ToString()
where databaseRow["contract_no"].ToString().StartsWith(excelRow["vend_contract_no"].ToString())
select new { databaseRow, excelRow }
foreach (var m in matches) {
var dst = m.excelRow;
var src = m.databaseRow;
dst["wpd_expiration_date"] = Convert.ToDateTime(src["con_end_date"]).Date;
dst["wpd_cont_cost"] = src["contract_cost"];
dst["wpd_contract_no"] = src["contract_no"];
dst["wpd_item_id"] = src["supplier_part_no"];
dst["wpd_item_no"] = src["item_id"];
dst["discontinued"] = src["discontinued"];
dst["job_no"] = src["job_no"];
}
}
我实际上并没有多少使用交叉连接,但我认为它们使用了一个哈希表来获得O(n + m)复杂度而不是O(n * m)。如果两个表都在数据库中,那么数据库可以利用已经构造的哈希表/索引。
您可能还想考虑某种生成的Linq2SQL类,因此您可以对行字段进行类型安全访问。
答案 1 :(得分:0)
您可以散列要匹配的值并具有O(m + n)复杂度。
你必须创建一个解析双方并创建一对(或一组)统一结果然后可以进行哈希处理的函数。例如,如果您的contract_no
一侧有一些已知格式的前缀而另一侧没有前缀,则可以删除前缀和散列。
答案 2 :(得分:0)
在找到匹配的部件号后,您应该能够通过退出内循环将时间缩短一半。也就是说,将break
作为部件号匹配时执行的代码中的最后一个语句。
但是,永远的一半仍然永远。
只有40,000行,您可以轻松地填充包含合同号和部件号作为键的字典,并将转换表行索引作为值。类似的东西:
Dictionary<string, int> conversionLookup = new Dictionary<string, int>();
for (int i = 0; i < dtConversion.Rows.Count; ++j)
{
conversionLookup.Add(string.Format("{0}:{1}",
dtConversion.Rows[j]["vend_contract_no"].ToString(),
dtConversion.Rows[j]["vend_item_id"].ToString()), j);
}
然后,你的外环变为:
for (int i = 0; i < dtFromTableContracts.Rows.Count; i++)
{
partNumber2 = dtFromTableContracts.Rows[i]["supplier_part_no"].ToString();
contract_no2 = dtFromTableContracts.Rows[i]["contract_no"].ToString();
string lookup = string.Format("{0}:{1}", contract_no2, partNumber2);
int ix;
if (conversionLookup(lookup, out ix))
{
// update dtConversion.Rows[ix]
}
}
我不熟悉您的部件号码的限制(在内循环中使用StartsWith
而不是等于),因此您可能需要稍微调整一下索引。
那应该非常快。
如果您无法创建使用合同编号进行直接查找的密钥,则可以通过创建包含合同编号和部件编号的List
以及二进制搜索来完成类似的操作。那将是O(m log n)而不是O(m * n)。还是要快一点。
答案 3 :(得分:0)
Trie是查找子字符串的最快结构。
答案 4 :(得分:0)
如果ContractTable.ContractNo具有已知的常量长度,那么您在(事实上)两个表之间存在PK-FK关系:
ContractTable.ContractNo = substring(Conversion.VendContractNo,1,K)
ContractTable.PartNumber = Conversion.PartNumber
其中K == Length(ContractTabel.ContractNo)。
对此密钥结构上的两个表进行索引将允许在O(Log N)+ O(Log M)时间内进行匹配,索引创建为O(N * Log N)+ O(M * Log M)时间。 Hashing可以进一步改善这一点,具体取决于良好哈希的构造。