【问题标题】:Arrays, matching, sorting数组、匹配、排序
【发布时间】:2016-02-14 00:59:31
【问题描述】:

我有两个数据集,每个数据集在 3 个单独的列中,位于同一张工作表中。

第一个数据集包含发票数据,第二个数据集包含发票中包含的各个项目的数据。

  • 第一个数据集(发票):CustomerNumber(长)、InvoiceDate(日期)、InvoiceAmount(双)
  • 第二个数据集(项目):CustomerNumber(长)、ItemPurchaseDate(日期)、ItemCost(双)

没有唯一的标识符来关联两个数据集。

一般规则是:

  • CustomerNumber=X 且 ItemPurchaseDate ItemPurchaseDate 的 Invoice 相关,但 InvoiceDate

(试想一下您尝试将商品与最近的发票相匹配的情况。)

比如这样

因此,并非所有项目都与发票相关,有时我们将无法找到发票的任何项目。

我编写了代码,但效率低下,因为它将遍历所有发票的所有项目。在我的例子中,有 9000 张发票和 26 万件物品。

但是为了演示(我省略了不重要的部分):

For Each InvCustNo In InvList 'look into every Invoice
    For Each ItemCustNo In ItemList 'look into every Item
        'check if we're looking at the new client,
        ' if yes - reset the date holder
        If prevInvCustNo <> InvCustNo Then
            prevInvDate = 0
        End If
        InvDate = InvCustNo.Offset(0, 1).Value 'define the InvoiceDate
        ItemDate = ItemCustNo.Offset(0, 1).Value 'define ItemDate
        If InvCustNo = ItemCustNo And InvDate > ItemDate And _
            InvDate >= prevInvDate And _
            prevInvDate <= ItemDate Then 'perform comparison
            TempSum = TempSum + ItemCustNo.Offset(0, 2)
        End If
    Next ItemCustNo
    prevInvDate = InvDate 'update the date-1
    prevInvCustNo = InvCustNo 'update the client-1
    InvCustNo.Offset(0, 7).Value = TempSum 'print values
    TempSum = 0 'reset sum
Next InvCustNo

上面的代码应该可以完成工作,但不是我拥有的数据量,主要有两个原因:

  • 它将经过 9.000 * 260.000 行 = 23.4 亿次操作
  • 它将一次写入 9000 个结果

想到了两个问题

  • 我可以一次性写出所有结果吗? (数组?)
  • 我能不能以某种方式限制我的匹配,这样我就不会做 23.4 亿次操作(我想我可以通过排序)

任何想法和示例(我可以用作起点)都会非常有帮助。

【问题讨论】:

  • no unique identifier that could relate two data sets 客户号呢?
  • 您是否考虑过 MS Access 和查询来解决此问题?正如@findwindow 指出的那样你确实有一个唯一的标识符
  • @ScottHoltzman 对不起,也许我的问题不够清楚。我没有唯一标识符,因为 CustomerNumber 在许多发票和许多项目上重复出现。仅通过使用 CustomerNumber,我无法将项目与发票相关联。
  • 我明白你的意思,但你有没有想过 Access 和 SQL 的强大功能?由于您确实有唯一的客户标识符,并且您可以根据您的逻辑将日期联系在一起?
  • @ScottHolzman 我可以使用 Access 和 SQL,但我的想法是在 Excel 中进行,因为必须进行一些额外的计算和格式化,而 SQL 不适合。

标签: vba excel


【解决方案1】:

我有一个我认为可能对你有用的非 vba 解决方案。

鉴于屏幕截图中的数据(反映了您的示例)

确保列 A:C 中的数据已排序

  • 按客户编号从 A 到 Z
  • 然后按 InvoiceDate 从最新到最旧

然后将以下公式放入G1(并向下拖动):

=IF(E2>MAX(IF(A:A=D2,B:B)),"No Invoice Yet",INDEX(B:B,(MATCH(E2,IF(A:A=D2,B:B),-1))))

作为一个数组(按 Ctrl + Shft + Enter),而不仅仅是 Enter。

公式的工作方式如下

  1. 将 ItemDate 与给定客户编号的 Max InvoiceDate 进行比较。如果 ItemDate 比最新发票日期新,则返回“No Invoice Yet”
  2. 否则,它会查找与 ItemDate 最接近的 InvoiceDate(考虑 ItemDate 之后的下一个可能的 InvoiceDate),用于相应的客户编号,并返回此日期。

【讨论】:

  • 索引/匹配再次出现^^