VB.NET/C# WinForms中用LINQ优化DataTable发票汇总性能
高效汇总DataTable发票组数据并更新原表
需求背景
现有包含商品成本、税费等字段的DataTable,其中supplier_id_invoice_number_combo列值相同的行属于同一发票组,需按该列:
- 汇总
total_cost_price_of_item列得到商品总成本 - 计算每组的总税费(CGST金额+SGST金额,按行内税率和成本计算后求和)
- 将上述两个汇总值更新到原表对应发票组的所有行的
invoice_item_amount和invoice_tax_amount字段
当前嵌套循环遍历的方式性能低下(130+行时已明显卡顿),需优化实现方式。
高效实现方案
方案1:使用LINQ分组计算(VB.NET)
通过LINQ一次性完成分组与汇总计算,再遍历原表更新,仅需两次遍历(分组一次,更新一次):
' 首先通过LINQ分组计算每个发票组的汇总值 Dim invoiceSummaries = From row In my_format_dt.AsEnumerable() Group row By combo = row.Field(Of String)("supplier_id_invoice_number_combo") Into Group Select New With { .Combo = combo, .TotalItemAmount = Math.Round(Group.Sum(Function(r) r.Field(Of Double)("total_cost_price_of_item")), 2), .TotalTaxAmount = Math.Round(Group.Sum(Function(r) (r.Field(Of Double)("cgst_percentage") / 100 * r.Field(Of Double)("total_cost_price_of_item")) + (r.Field(Of Double)("sgst_percentage") / 100 * r.Field(Of Double)("total_cost_price_of_item")) ), 2) } ' 将汇总结果转成字典,方便快速查找(可选,进一步提升更新效率) Dim summaryDict = invoiceSummaries.ToDictionary(Function(s) s.Combo) ' 遍历原表更新对应字段 For Each row In my_format_dt.AsEnumerable() Dim combo = row.Field(Of String)("supplier_id_invoice_number_combo") If summaryDict.ContainsKey(combo) Then row.SetField("invoice_item_amount", summaryDict(combo).TotalItemAmount) row.SetField("invoice_tax_amount", summaryDict(combo).TotalTaxAmount) End If Next
方案2:使用字典缓存汇总结果(更直观的O(N)复杂度)
先遍历一次原表计算所有发票组的汇总值并存入字典,再遍历一次原表完成更新,全程仅两次遍历:
Dim summaryDict As New Dictionary(Of String, (TotalItem As Double, TotalTax As Double)) ' 第一次遍历:计算每个发票组的汇总值 For Each row In my_format_dt.AsEnumerable() Dim combo = row.Field(Of String)("supplier_id_invoice_number_combo") Dim itemCost = row.Field(Of Double)("total_cost_price_of_item") Dim cgstAmt = (row.Field(Of Double)("cgst_percentage") / 100) * itemCost Dim sgstAmt = (row.Field(Of Double)("sgst_percentage") / 100) * itemCost If summaryDict.ContainsKey(combo) Then summaryDict(combo) = ( summaryDict(combo).TotalItem + itemCost, summaryDict(combo).TotalTax + cgstAmt + sgstAmt ) Else summaryDict.Add(combo, (itemCost, cgstAmt + sgstAmt)) End If Next ' 对字典中的汇总值做四舍五入 For Each key In summaryDict.Keys.ToList() summaryDict(key) = ( Math.Round(summaryDict(key).TotalItem, 2), Math.Round(summaryDict(key).TotalTax, 2) ) Next ' 第二次遍历:更新原表字段 For Each row In my_format_dt.AsEnumerable() Dim combo = row.Field(Of String)("supplier_id_invoice_number_combo") If summaryDict.ContainsKey(combo) Then row.SetField("invoice_item_amount", summaryDict(combo).TotalItem) row.SetField("invoice_tax_amount", summaryDict(combo).TotalTax) End If Next
性能优化说明
- 原实现的时间复杂度为O(M*N)(M为唯一发票组数量,N为总行数),当数据量增大时性能急剧下降
- 两种新方案的时间复杂度均为O(N)(字典查找为O(1)),仅需2次遍历原表,性能提升显著
- 推荐使用方案2,逻辑更直观,避免LINQ分组可能带来的微小性能开销
内容的提问来源于stack exchange,提问作者Ankit Srivastava
相关产品推荐
相关产品推荐

