根据多个条件高效地复制和粘贴单元格块
Efficiently copy and paste block of cells based on multiple criteria
我有一个大型数据集,想根据一组条件查询到另一个 sheet 的列。我有一个工作方法,它使用一个循环来查找开始和一个循环来查找结束以获得所需的行,但这很慢。
我想避免使用查找函数所需的唯一列,因为这会使数据末尾很难找到。我考虑过尝试使用某种 FindAll 函数,但似乎无法弄清楚如何启动它。
我已经看到很多关于过滤器的信息,但这些似乎是通过复制整行来工作的,我想避免这种情况。
就像我说的,这段代码工作得很好,但它会变慢,因为它在模型 运行 期间会 运行 1000 次。我也有几个类似的潜艇,我希望能够解决这个问题
Sub Join(CI, FI, FSD)
Dim a, b, LastRow As Long
LastRow = Fcst_Cust.Range("a1048576").End(xlUp).Row + 1
'find all values for that customer ID for dates greater than the forecast start date and copy onto forecast tab.
a = 3
b = 2
Do Until ((Raw_IFcst.Cells(b + 1, 2) = CI) And (Raw_IFcst.Cells(b + 1, 3) >= FSD))
a = a + 1
b = b + 1
Loop
Do Until Raw_IFcst.Cells(b + 1, 2) <> CI
b = b + 1
Loop
Raw_IFcst.Range("A" & a & ":AZ" & b).Copy
Fcst_Cust.Range("C" & LastRow).PasteSpecial xlPasteValues
Raw_IFcst.Range("BB" & a & ":CW" & b).Copy
Fcst_Cust.Range("BG" & LastRow).PasteSpecial xlPasteValues
End Sub
Range 对象的 AdvancedFilter 方法可能是最有效的...而且它绝对不需要复制整行。您可以选择所需的列。
但是,我重构了您的代码,但没有使用 AdvancedFilter。这应该快得多:
Sub Join_(CI, FI, FSD)
Dim a&, b&, LastRow&, v
LastRow = Fcst_Cust.[a1048576].End(xlUp).Row + 1
a = 3
b = 2
With Raw_IFcst
v = .Cells(1, 2).Resize(.[b1048576].End(xlUp).Row, 2).Value2
Do
If If v(b + 1, 1) = CI Then
If v(b + 1, 2) >= FSD Then
Exit Do
End If
End If
a = a + 1
b = b + 1
Loop
Do Until v(b + 1, 1) <> CI
b = b + 1
Loop
.Range("A" & a & ":AZ" & b).Copy
Fcst_Cust.Range("C" & LastRow).PasteSpecial xlPasteValues
.Range("BB" & a & ":CW" & b).Copy
Fcst_Cust.Range("BG" & LastRow).PasteSpecial xlPasteValues
End With
End Sub
注意:我还没有测试过这个,所以请做。
注意:我已经更改了 Sub 的名称。使用现有 VBA 函数的名称作为过程的名称不是一个好的做法,除非您确实希望覆盖该函数。
注意:我更改了 Dim 行。您有 a
和 b
作为变体,而它们应该是多头。
注意:最让您的程序变慢的是逐个单元格读取第 2 列和第 3 列。一次读取和写入一个单元格可能是 Excel 开发商。我的代码所做的是将第 2 列和第 3 列的已用部分传输到数组 v
中。这是非常快的,并且访问数组的各个元素而不是单个单元格非常快。
注意:我更改了第一个 Do While
循环。 VBA 表达式求值不会短路多个子句。因此,如果 clause 1
和 clause 2
必须为真(如您的情况),那么即使 clause 1
为假,clause 2
也会被评估。那是零收益的加工浪费。在您的原始设置中,由于单个细胞的读取速度较慢,浪费被放大了。优化是将子句分成单独的行。这样,如果第一个失败,则永远不会评估第二个。通过将最有可能失败的子句放在第一行,可以进一步优化这个概念。我不知道在你的场景中哪个最有可能失败,所以我把你的第一个子句放在第一行。
我有一个大型数据集,想根据一组条件查询到另一个 sheet 的列。我有一个工作方法,它使用一个循环来查找开始和一个循环来查找结束以获得所需的行,但这很慢。
我想避免使用查找函数所需的唯一列,因为这会使数据末尾很难找到。我考虑过尝试使用某种 FindAll 函数,但似乎无法弄清楚如何启动它。
我已经看到很多关于过滤器的信息,但这些似乎是通过复制整行来工作的,我想避免这种情况。
就像我说的,这段代码工作得很好,但它会变慢,因为它在模型 运行 期间会 运行 1000 次。我也有几个类似的潜艇,我希望能够解决这个问题
Sub Join(CI, FI, FSD)
Dim a, b, LastRow As Long
LastRow = Fcst_Cust.Range("a1048576").End(xlUp).Row + 1
'find all values for that customer ID for dates greater than the forecast start date and copy onto forecast tab.
a = 3
b = 2
Do Until ((Raw_IFcst.Cells(b + 1, 2) = CI) And (Raw_IFcst.Cells(b + 1, 3) >= FSD))
a = a + 1
b = b + 1
Loop
Do Until Raw_IFcst.Cells(b + 1, 2) <> CI
b = b + 1
Loop
Raw_IFcst.Range("A" & a & ":AZ" & b).Copy
Fcst_Cust.Range("C" & LastRow).PasteSpecial xlPasteValues
Raw_IFcst.Range("BB" & a & ":CW" & b).Copy
Fcst_Cust.Range("BG" & LastRow).PasteSpecial xlPasteValues
End Sub
Range 对象的 AdvancedFilter 方法可能是最有效的...而且它绝对不需要复制整行。您可以选择所需的列。
但是,我重构了您的代码,但没有使用 AdvancedFilter。这应该快得多:
Sub Join_(CI, FI, FSD)
Dim a&, b&, LastRow&, v
LastRow = Fcst_Cust.[a1048576].End(xlUp).Row + 1
a = 3
b = 2
With Raw_IFcst
v = .Cells(1, 2).Resize(.[b1048576].End(xlUp).Row, 2).Value2
Do
If If v(b + 1, 1) = CI Then
If v(b + 1, 2) >= FSD Then
Exit Do
End If
End If
a = a + 1
b = b + 1
Loop
Do Until v(b + 1, 1) <> CI
b = b + 1
Loop
.Range("A" & a & ":AZ" & b).Copy
Fcst_Cust.Range("C" & LastRow).PasteSpecial xlPasteValues
.Range("BB" & a & ":CW" & b).Copy
Fcst_Cust.Range("BG" & LastRow).PasteSpecial xlPasteValues
End With
End Sub
注意:我还没有测试过这个,所以请做。
注意:我已经更改了 Sub 的名称。使用现有 VBA 函数的名称作为过程的名称不是一个好的做法,除非您确实希望覆盖该函数。
注意:我更改了 Dim 行。您有 a
和 b
作为变体,而它们应该是多头。
注意:最让您的程序变慢的是逐个单元格读取第 2 列和第 3 列。一次读取和写入一个单元格可能是 Excel 开发商。我的代码所做的是将第 2 列和第 3 列的已用部分传输到数组 v
中。这是非常快的,并且访问数组的各个元素而不是单个单元格非常快。
注意:我更改了第一个 Do While
循环。 VBA 表达式求值不会短路多个子句。因此,如果 clause 1
和 clause 2
必须为真(如您的情况),那么即使 clause 1
为假,clause 2
也会被评估。那是零收益的加工浪费。在您的原始设置中,由于单个细胞的读取速度较慢,浪费被放大了。优化是将子句分成单独的行。这样,如果第一个失败,则永远不会评估第二个。通过将最有可能失败的子句放在第一行,可以进一步优化这个概念。我不知道在你的场景中哪个最有可能失败,所以我把你的第一个子句放在第一行。