VB.NET下如何按contentID关联对比两份Excel并生成合并结果Excel
按contentID关联匹配+数据对比的实现思路(适配你现有EPPlus代码)
核心逻辑前置
你现在的独立列存储方式没法高效按contentID匹配,要先把两份Excel的行数据改成以处理后的contentID为键的Dictionary存储,匹配时间从O(n*m)降到O(n+m),还能避免列错位问题。
第一步:改造现有读取逻辑,用Dictionary存行数据
统一contentID格式化规则(必做,否则大概率匹配失败)
先把所有读出来的contentID做统一处理,再当字典键:
- 去首尾空格:
Dim cid = rawCellValue.ToString().Trim() - 数字型contentID转无格式字符串:如果是数字,转成整数/字符串,去掉
.0后缀,比如把123.0转成"123" - 带字母的统一大小写:比如全转小写
cid = cid.ToLower()
定义字典存储两份表的行数据
' 1. 存储第一份「交付表」(含delivery number、contentID、packages、volume) ' 值的结构:(DeliveryNo As String, Packages As String, Volume As String) Dim deliveryDict As New Dictionary(Of String, (DeliveryNo As String, Packages As String, Volume As String)) ' 2. 存储第二份「SPS表」(含SPS Number、folder number、contentID、packages、volume) Dim spsDict As New Dictionary(Of String, (SPSNo As String, FolderNo As String, Packages As String, Volume As String))
读Excel行时写入字典
读每一行时,先拿处理后的contentID,再把该行其他字段存进字典,重复contentID的处理逻辑按需加:
' 示例:读交付表的每一行 For Each row In deliveryWorksheet.Rows ' 跳过空行 If row.Cells("contentID").Text.Trim() = "" Then Continue For ' 处理contentID当字典键 Dim cidKey = row.Cells("contentID").Text.Trim() ' 如果是数字型contentID,额外处理:比如把123.0转成123 Dim cidNum As Decimal If Decimal.TryParse(cidKey, cidNum) Then cidKey = cidNum.ToString("0") ' 重复contentID判断(按需选择覆盖/跳过/报错) If deliveryDict.ContainsKey(cidKey) Then ' 这里选择覆盖:或者写日志记录重复值 Console.WriteLine($"交付表重复contentID:{cidKey},已用最新行覆盖") End If ' 写入字典 deliveryDict(cidKey) = ( DeliveryNo:= row.Cells("delivery number").Text.Trim(), Packages:= row.Cells("packages").Text.Trim(), Volume:= row.Cells("volume").Text.Trim() ) Next
SPS表的读取逻辑完全同理,对应存SPS Number、folder number到spsDict即可。
第二步:关联匹配+对比逻辑
先合并所有contentID去重
遍历两个字典的所有键,覆盖只在SPS表、只在交付表、两边都有的三种场景:
' 合并两个表的所有contentID,去重后遍历 Dim allCids = spsDict.Keys.Union(deliveryDict.Keys)
逐行生成结果Excel的数据
你已经初始化了结果表的表头,接下来从第2行开始写数据:
' 结果表当前行号,从2开始(第1行是表头) Dim resultRowNum As Integer = 2 For Each cid In allCids ' 先初始化所有列的默认值 Dim spsNo As String = "" Dim folderNo As String = "" Dim deliveryNo As String = "" Dim packages As String = "" Dim volume As String = "" Dim pkgDiff As Boolean = False Dim volDiff As Boolean = False ' 1. 从SPS表取数据 Dim hasSps As Boolean = spsDict.ContainsKey(cid) If hasSps Then spsNo = spsDict(cid).SPSNo folderNo = spsDict(cid).FolderNo End If ' 2. 从交付表取数据,同时对比packages/volume Dim hasDelivery As Boolean = deliveryDict.ContainsKey(cid) If hasDelivery Then deliveryNo = deliveryDict(cid).DeliveryNo ' 两边都有数据时做对比 If hasSps Then ' 注意:数值类的packages/volume一定要转Decimal再比,不要比字符串 Dim spsPkg As Decimal, delPkg As Decimal Decimal.TryParse(spsDict(cid).Packages, spsPkg) Decimal.TryParse(deliveryDict(cid).Packages, delPkg) pkgDiff = spsPkg <> delPkg Dim spsVol As Decimal, delVol As Decimal Decimal.TryParse(spsDict(cid).Volume, spsVol) Decimal.TryParse(deliveryDict(cid).Volume, delVol) volDiff = spsVol <> delVol ' 按需设置packages/volume的展示内容:比如差异时标注两边的值,一致就直接写 packages = If(pkgDiff, $"差异(SPS:{spsDict(cid).Packages} / 交付:{deliveryDict(cid).Packages})", spsDict(cid).Packages) volume = If(volDiff, $"差异(SPS:{spsDict(cid).Volume} / 交付:{deliveryDict(cid).Volume})", spsDict(cid).Volume) Else ' 只有交付表有数据,直接写交付表的值 packages = deliveryDict(cid).Packages volume = deliveryDict(cid).Volume End If Else ' 只有SPS表有数据,直接写SPS表的值 If hasSps Then packages = spsDict(cid).Packages volume = spsDict(cid).Volume End If End If ' 写入结果表对应单元格(对应需求的表头顺序:SPS number、folder number、delivery number、contentID、packages、volume) resultWorksheet.Cells(resultRowNum, 1).Value = spsNo resultWorksheet.Cells(resultRowNum, 2).Value = folderNo resultWorksheet.Cells(resultRowNum, 3).Value = deliveryNo resultWorksheet.Cells(resultRowNum, 4).Value = cid resultWorksheet.Cells(resultRowNum, 5).Value = packages resultWorksheet.Cells(resultRowNum, 6).Value = volume ' 可选:给差异单元格加背景色标记 If pkgDiff Then resultWorksheet.Cells(resultRowNum, 5).Style.Fill.PatternType = OfficeExcelFillStyle.Solid resultWorksheet.Cells(resultRowNum, 5).Style.Fill.BackgroundColor.SetColor(Color.LightPink) End If If volDiff Then resultWorksheet.Cells(resultRowNum, 6).Style.Fill.PatternType = OfficeExcelFillStyle.Solid resultWorksheet.Cells(resultRowNum, 6).Style.Fill.BackgroundColor.SetColor(Color.LightPink) End If ' 行号递增 resultRowNum += 1 Next
初学者必踩坑提醒
- 不要直接对比字符串型的数值:Excel读出来的packages/volume如果是数字,直接比字符串会出现
"1.0"和"1"不匹配的假差异,必须转Decimal再比较 - contentID格式化必须统一:比如带空格、大小写、数字带小数点的情况不处理,会出现大量匹配不上的问题
- 空contentID直接跳过:不要把空值当字典键,避免匹配混乱
- 重复contentID提前处理:不要默认同一个Excel里contentID唯一,加判断逻辑避免数据被意外覆盖
内容的提问来源于stack exchange,提问作者JOHN Doe
相关产品推荐
相关产品推荐

