Excel VBA如何根据条件将某些列的数据从一个表复制到另一个表?

mu0hgdu0  于 2023-06-07  发布在  其他
关注(0)|答案(1)|浏览(315)

在Excel中使用VBA,如何根据表1单行中的名称列中的值将表2中某些列的数据复制到表3中?
表2(原始数据,位于工作表2)
| 日期1|日期2|日期3|姓名|文本|
| - -----|- -----|- -----|- -----|- -----|
| 日期|日期|日期|默认|正文|
| 日期|日期|日期|默认|正文|
| 日期|日期|日期|默认|正文|
| 日期|日期|日期|乔恩·多伊|正文|
| 日期|日期|日期|乔恩·多伊|正文|
| 日期|日期|日期|乔恩·多伊|正文|
| 日期|日期|日期|乔恩·多伊|正文|
| 日期|日期|日期|无名氏|正文|
| 日期|日期|日期|无名氏|正文|
| 日期|日期|日期|无名氏|正文|
| 日期|日期|日期|无名氏|正文|
| 日期|日期|日期|无名氏|正文|
| 日期|日期|日期|无名氏|正文|

示例1:

表1(表1只有1行数据,位于Worksheet 1中)
| 不相关数据1|不相关数据2|不相关数据3|不相关数据4|姓名|
| - -----|- -----|- -----|- -----|- -----|
| 随机数据|其他数据|更多数据|一些数据|无名氏|
表3(所需输出,位于工作表1中,行仅为表2中的John Doe行)
| 已选定|日期1|日期2|日期3|文本|
| - -----|- -----|- -----|- -----|- -----|
| | 日期|日期|日期|正文|
| | 日期|日期|日期|正文|
| | 日期|日期|日期|正文|
| | 日期|日期|日期|正文|

示例二:

表1(表1只有1行数据,位于Worksheet 1,名称为空)
| 不相关数据1|不相关数据2|不相关数据3|不相关数据4|姓名|
| - -----|- -----|- -----|- -----|- -----|
| 随机数据|其他数据|更多数据|一些数据||
表3(所需输出,位于工作表1中,行仅为表2中的默认行)
| 已选定|日期1|日期2|日期3|文本|
| - -----|- -----|- -----|- -----|- -----|
| | 日期|日期|日期|正文|
| | 日期|日期|日期|正文|
| | 日期|日期|日期|正文|
下面的解决方案(来自VBA Copying data from one table to another and rearranging columns)几乎可以满足我的需要,除了我需要能够根据Table 1中的名称过滤Table 2中的数据,如果名称为空,则使用Table 2中的Default数据。谢谢你的帮助!

Option Explicit

    Sub raw2processed()

    Dim lc As Long, mc As Variant, x As Variant
    Dim raw_data As Worksheet, processed_data As Worksheet
    Dim raw_tbl As ListObject, processed_tbl As ListObject

    Set raw_data = Worksheets("raw")
    Set processed_data = Worksheets("processed")
    Set raw_tbl = raw_data.ListObjects("tbl_raw")
    Set processed_tbl = processed_data.ListObjects("tbl_processed")

    With processed_tbl
        'clear target table
        On Error Resume Next
        .DataBodyRange.Clear
        .Resize .Range.Resize(raw_tbl.ListRows.Count + 1, .ListColumns.Count)
        On Error GoTo 0

        'loop through target header and collect columns from raw_tbl
        For lc = 1 To .ListColumns.Count
            Debug.Print .HeaderRowRange(lc)
            mc = Application.Match(.HeaderRowRange(lc), raw_tbl.HeaderRowRange, 0)
            If Not IsError(mc) Then
                x = raw_tbl.ListColumns(mc).DataBodyRange.Value
                .ListColumns(lc).DataBodyRange = x
            End If
        Next lc

    End With

    End Sub
6pp0gazn

6pp0gazn1#

如果要在传输数据之前使用过滤器,则需要事先将其应用于源原点。
你可以用Autofilter这样做:

'Filter the data to use only supplied Name
    Dim FilterColumn As Long
    FilterColumn = Application.Match(FilterName, SourceTable.HeaderRowRange, 0)
    SourceTable.DataBodyRange.AutoFilter Field:=FilterColumn, Criteria1:=Criteria

过滤器所做的基本上是隐藏不匹配条件的行(零高度),因此当您传输数据时,您需要确保仅使用可见的行(例如.SpecialCells(xlCellTypeVisible))。
将所有这些放在一起将给予:

Sub Test()

    'Define your main tables
    Dim SourceTable As ListObject
    Set SourceTable = ThisWorkbook.Worksheets("Sheet2").ListObjects("Table2")
    
    Dim DestTable As ListObject
    Set DestTable = ThisWorkbook.Worksheets("Sheet3").ListObjects("Table3")
    
    'Define the filter values
    Dim RefTable As ListObject
    Set RefTable = ThisWorkbook.Worksheets("Sheet1").ListObjects("Table1")
    
    Dim FilterName As String
    FilterName = "Name"
    
    'Define filter
    Dim NameValue As String, col As Long
    col = Application.Match("Name", RefTable.HeaderRowRange, 0)
    NameValue = RefTable.DataBodyRange.Cells(1, col)
    
    If NameValue = "" then
        NameValue = "Default"
    End If

    CopyFilteredTable FilterName, NameValue, SourceTable, DestTable

End Sub

Sub CopyFilteredTable(ByVal FilterName As Variant, ByVal Criteria As Variant, SourceTable As ListObject, DestTable As ListObject)
   
    'Filter the data to use only supplied criteria
    Dim FilterColumn As Long
    FilterColumn = Application.Match(FilterName, SourceTable.HeaderRowRange, 0)
    SourceTable.DataBodyRange.AutoFilter Field:=FilterColumn, Criteria1:=Criteria
    
    With DestTable
    
        'Clear destination table
        On Error Resume Next
            .DataBodyRange.Clear
            .Resize .Range.Resize(SourceTable.ListRows.SpecialCells(xlCellTypeVisible).Count + 1, .ListColumns.Count)
        On Error GoTo 0

        'Loop through target header and collect columns from Source Table
        Dim lc As Long
        For lc = 1 To .ListColumns.Count
            
            Dim mc As Variant
            mc = Application.Match(.HeaderRowRange(lc), SourceTable.HeaderRowRange, 0)
                        
            If Not IsError(mc) Then
            
                Dim ColRange As Range
                Set ColRange = SourceTable.ListColumns(mc).DataBodyRange.SpecialCells(xlCellTypeVisible)
                
                .ListColumns(lc).DataBodyRange.Resize(ColRange.Rows.Count, ColRange.Columns.Count).Value2 = ColRange.Value2
                
            End If
            
        Next lc

    End With

End Sub

之前

之后

请注意,这将使您的源表处于过滤模式。如果有问题,你可以在最后加上SourceTable.AutoFilter.ShowAllData
编辑1:如果你想保留格式,你可以使用Copy方法,而不是只传输值,但请注意,这样会更慢。

ColRange.Copy Destination:=.ListColumns(lc).DataBodyRange.Resize(ColRange.Rows.Count, ColRange.Columns.Count)

EDIT2:要处理引用名称与源表中的任何名称都不匹配的情况,可以在过滤器后添加一个检查,如果过滤后的表中不存在数据,则使用“Default”过滤器重新运行过滤器。

On Error Resume Next
        Dim test As String
        test = SourceTable.DataBodyRange.SpecialCells(xlCellTypeVisible).Address
        If Err.Number = 1004 Then 'No cells were found.
            SourceTable.DataBodyRange.AutoFilter Field:=FilterColumn, Criteria1:="Default"
        Else
            Err.Raise Err.Number, Err.Source, Err.Description
        End If
    On Error GoTo 0

相关问题