【问题标题】:VBA to copy specific cells from one worksheet to another upon meeting a criteriaVBA在满足条件时将特定单元格从一个工作表复制到另一个工作表
【发布时间】:2021-08-24 16:17:49
【问题描述】:

我的 VBA 知识非常有限,所以在这里寻求帮助。尝试了一些谷歌搜索并将代码放在一起,但没有达到目标。在这里感谢您的帮助!

我有 2 个工作表:

  1. 数据 - 包含要复制的数据的源工作表
  2. 仪表板 - 用于粘贴的目标表

数据表 - 它有多个列,我命名的那些是我需要复制的,除了名为“Sold?”的列这是标准。图像中没有名称的其他列实际上有数据,为了避免混淆,我在这里删除了它们。 这张表会变大,我会在需要时添加一行新数据。

仪表板表 - 当我单击“刷新”按钮时,我希望代码检查“数据”表以及一行是否满足已售条件? =“N”,则只有 C、G、J、M 列中的数据应复制并粘贴到“仪表板”表的 B、C、D、E 列中。附加标准:如果投资名称重复,则需要汇总详细信息并显示在仪表板表中。我在图像中提供了我的预期输出。 (ABC & TY 总结)

我尝试了一些,但无法合并所有条件,并且此代码在运行时不会抛出错误但什么也不做,没有输出。

Private Sub Refresh_Click()

Worksheets("Dashboard").Activate

Application.ScreenUpdating = True

a = Worksheets("Data").Cells(Rows.Count, "A").End(xlUp).Row

For i = 12 To a
If Worksheets("Data").Cells(i, 15).Value = "N" Then
Worksheets("Data").Cells(i, 3).Copy
Worksheets("Data").Cells(i, 7).Copy
Worksheets("Data").Cells(i, 13).Copy
Worksheets("Data").Cells(i, 14).Copy

Worksheets("Dashboard").Activate
Worksheets("Dashboard").Range("B6:G25").Select
ActiveSheet.Paste
End If
Next

Application.CutCopyMode = False

End Sub

【问题讨论】:

  • 为什么不是pivot table?无论如何:代码中的一个问题是您对 Range.Copy 的使用。现在代码只会复制最后一个单元格(第 14 列的单元格)。您必须一次复制所有单元格。
  • 这里不需要vba编码。正如 Evil Blue Monkey 所说,数据透视表是您的解决方案。
  • 感谢您的回复。是的,我确实考虑过数据透视表,但我在这里提到的“数据”表,我有一些类似的表(相同的格式),但数据不同。每张纸的数据透视表使文件变得庞大。因此,考虑有一个可以跨工作表复制的代码以提取值并显示在“仪表板”工作表中。希望这是有道理的。

标签: excel vba copy copy-paste


【解决方案1】:

我强烈建议使用数据透视表。不过,如果您想要基于 VBA 的解决方案,您可以尝试以下代码:

Option Explicit

Private Sub Refresh_Click()
    
    'Declarations.
    Dim BlnHiddenColumns() As Boolean
    Dim DblFirstRow As Double
    Dim DblLastRow As Double
    Dim DblCounter01 As Double
    Dim DblCounterLimit01 As Double
    Dim DblInvestmentNameColumn As Double
    Dim DblQuantityColumn As Double
    Dim DblAfterChargeColumn As Double
    Dim DblCurrentPLColumn As Double
    Dim DblSoldColumn As Double
    Dim RngData As Range
    Dim RngResult As Range
    Dim StrAutofilterAddress As String
    Dim StrMarker As String
    Dim StrInvestmentNameHeader As String
    Dim StrQuantityHeader As String
    Dim StrAfterChargeHeader As String
    Dim StrCurrentPLHeader As String
    Dim WksData As Worksheet
    Dim WksDashboard As Worksheet
    Dim WksPivotTable As Worksheet
    Dim PvtPivotTable01 As PivotTable
    
    'Settings.
    DblInvestmentNameColumn = 3
    DblQuantityColumn = 7
    DblAfterChargeColumn = 10
    DblCurrentPLColumn = 13
    DblSoldColumn = 15
    DblFirstRow = 12
    DblCounterLimit01 = 1000
    StrMarker = "N"
    Set WksData = Worksheets("Data")
    DblLastRow = WksData.Cells(Rows.Count, "B").End(xlUp).Row
    Set RngData = WksData.Range(WksData.Cells(DblFirstRow - 1, Excel.WorksheetFunction.Min(DblInvestmentNameColumn, DblQuantityColumn, DblAfterChargeColumn, DblCurrentPLColumn, DblSoldColumn)), WksData.Cells(DblLastRow, Excel.WorksheetFunction.Max(DblInvestmentNameColumn, DblQuantityColumn, DblAfterChargeColumn, DblCurrentPLColumn, DblSoldColumn)))
    ReDim BlnHiddenColumns(1 To RngData.Columns.Count)
    Set WksDashboard = Worksheets("Dashboard")
    Set RngResult = WksDashboard.Range("B6")
    StrInvestmentNameHeader = WksDashboard.Range("B5").Value
    StrQuantityHeader = WksDashboard.Range("C5").Value
    StrAfterChargeHeader = WksDashboard.Range("D5").Value
    StrCurrentPLHeader = WksDashboard.Range("E5").Value
    
    'Turning off screen updating.
    Application.ScreenUpdating = False
    
    'Checking for any previous results list.
    If Excel.WorksheetFunction.CountBlank(RngResult) <> RngResult.Cells.Count Then
        DblCounter01 = 0
        
        'Checking each row of the result list until an entirely blank row is found.
        Do Until Excel.WorksheetFunction.CountBlank(RngResult.Offset(DblCounter01, 0)) = RngResult.Cells.Count
            
            DblCounter01 = DblCounter01 + 1
            
            'If the number of rows checked is equal or superior to DblCounterLimit01 the macro is terminated.
            If DblCounter01 >= DblCounterLimit01 Then
                MsgBox "Please clear the current holdings list manually", vbCritical + vbOKOnly, "Unable to clear the current list"
                Exit Sub
            End If
            
        Loop
        
        'Clearing the list.
        RngResult.Parent.Range(RngResult, RngResult.Offset(DblCounter01 - 1)).ClearContents
        
    End If
    
    'Checking for existing autofilter in WksData.
    If WksData.AutoFilterMode = True Then
        'Coping the address of the autofilter in WksData.
        StrAutofilterAddress = WksData.AutoFilter.Range.Address
    End If
    
    'Removing any autofilter in WksData.
    WksData.AutoFilterMode = False
    
    'Covering each column of RngData.
    For DblCounter01 = 1 To RngData.Columns.Count
        
        'Setting BlnHiddenColumns accordingly to the RngData columns' status (hidden/not hidden).
        BlnHiddenColumns(DblCounter01) = RngData.Columns(DblCounter01).Hidden
        
        'Hiding the columns of RngData we won't copy.
        Select Case DblCounter01 + RngData.Column - 1
            Case Is = DblInvestmentNameColumn, DblQuantityColumn, DblAfterChargeColumn, DblCurrentPLColumn
                RngData.Columns(DblCounter01).Hidden = False
            Case Else
                RngData.Columns(DblCounter01).Hidden = True
        End Select
    Next
    
    'Filtering RngData.
    RngData.AutoFilter Field:=DblSoldColumn - RngData.Column + 1, Criteria1:=StrMarker
    
    'Copying the filtered RngData into RngResult.
    RngData.Resize(RngData.Rows.Count - 1, RngData.Columns.Count).Offset(1, 0).SpecialCells(xlCellTypeVisible).Copy RngResult
    
    'Restoring the RngData columns to their previous status (hidden/not hidden).
    For DblCounter01 = 1 To RngData.Columns.Count
        If BlnHiddenColumns(DblCounter01) Then
            RngData.Columns(DblCounter01).Hidden = True
        Else
            RngData.Columns(DblCounter01).Hidden = False
        End If
    Next
    
    'Removing any autofilter in WksData.
    WksData.AutoFilterMode = False
    
    'Restoring any pre-existing autofilter in WksData.
    If StrAutofilterAddress <> "" Then
        WksData.Range(StrAutofilterAddress).AutoFilter
    End If
    
    'Setting RngResult to cover the imported list (headers included).
    Set RngResult = RngResult.Offset(-1, 0)
    Set RngResult = WksDashboard.Range(RngResult, RngResult.End(xlDown).End(xlToRight))
    
    'Creating WksPivotTable.
    Set WksPivotTable = Sheets.Add
    
    'Creating PvtPivotTable01.
    Set PvtPivotTable01 = ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, _
                                                            SourceData:=RngResult, _
                                                            Version:=7 _
                                                           ).CreatePivotTable(TableDestination:=WksPivotTable.Cells(1, 1), _
                                                                              TableName:="Temporary Pivot Table", _
                                                                              DefaultVersion:=7 _
                                                                             )
    
    'Setting PvtPivotTable01.
    With PvtPivotTable01.PivotFields(StrInvestmentNameHeader)
        .Orientation = xlRowField
        .Position = 1
    End With
    With PvtPivotTable01
        .AddDataField .PivotFields(StrQuantityHeader), "Sum of " & StrQuantityHeader, xlSum
        .AddDataField .PivotFields(StrAfterChargeHeader), "Sum of " & StrAfterChargeHeader, xlSum
        .AddDataField .PivotFields(StrCurrentPLHeader), "Sum of " & StrCurrentPLHeader, xlSum
        .ColumnGrand = False
    End With
    
    'Clearing the data from RngResult.
    RngResult.Offset(1, 0).Resize(RngResult.Rows.Count - 1).ClearContents
    
    'Copying the PvtPivotTable01 content to RngResult.
    PvtPivotTable01.DataBodyRange.Offset(0, -1).Resize(, PvtPivotTable01.DataFields.Count + 1).Copy RngResult.Cells(2, 1)
    
    'Deleting WksPivotTable.
    Application.DisplayAlerts = False
    WksPivotTable.Delete
    Application.DisplayAlerts = True
    
    'Restoring screen updating.
    Application.ScreenUpdating = False
    
End Sub

我故意让它比必要的更长,特别是通过创建许多变量来避免硬编码数据。这种方法在更复杂和/或更长的代码中可能很有用。

【讨论】:

  • 感谢您的代码。我尝试在“刷新”命令按钮中应用它并尝试运行它,但什么也没发生。仪表板工作表保持空白。没有给出错误。有什么想法吗?
  • 这很奇怪。我在宏的开头添加了一条开场消息。如果宏被正确调用,它就会出现。如果没有任何反应,则该按钮未正确链接到宏。请尝试代码并发送反馈。
  • 找出问题所在。数据表中的最后一行检查发生在 A 列实际上总是完全空白(更多的边框),因此将其更改为 B 列并且代码有效。
  • 虽然有几个问题: 1. 代码需要将近 15-20 秒才能运行,并且 Excel 进入无响应阶段。如果我要扩展此代码以从多个工作表中获取数据,它可能不会那么顺利。有什么办法可以优化代码? 2. 现在,它只是按原样从数据表中获取数据,但并未将它们汇总到投资名称上。这个可以加吗?
  • 1) 考虑到您有一个实际的列来标记您需要的数据,我们可能会对列表进行排序(或仅对其进行过滤)并复制我们需要的整个部分。我认为这应该使代码更快。再说一遍:数据透视表仍然应该是最好的解决方案。 2)我完全忘记了求和部分。可以添加。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-10
  • 1970-01-01
  • 2014-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多