【问题标题】:Create new worksheet for each unique value为每个唯一值创建新工作表
【发布时间】:2022-03-06 17:55:23
【问题描述】:

我有以下代码,可以很好地将相关数据复制到我的工作表中。我为 J 列中的每个唯一部门手动创建每个工作表,然后运行此宏。我想要一个宏,它可以根据 J 列中的唯一值动态创建工作表。我在网上找到了很好的资源,但是当它到达已经为其创建工作表的行时,我发现的那些似乎出错了。在手动创建其他工作表之前,我已经包含了我当前使用的代码以及库存表的屏幕截图

Sub CopyRows()

Dim bottomJ As Integer
bottomJ = Range("J" & Rows.Count).End(xlUp).Row
Dim c As Range
Dim ws As Worksheet
For Each c In Sheets("All Dept.").Range("J2:J" & bottomJ)
    For Each ws In Sheets
        ws.Activate
        If ws.Name = c Then
            c.EntireRow.Copy Cells(Rows.Count, "A").End(xlUp).Offset(1, 0)
            End If
        Next ws
    Next c

End Sub

【问题讨论】:

  • 你得到了什么错误,代码的哪一行?
  • 您是说对于工作表All Dept.J 列中的每个唯一值,您要创建一个包含相同表但仅包含相关数据的新工作表?要将这些工作表复制到现有工作簿还是新工作簿?如果以前存在任何工作表,是否应该将其删除然后重新创建,或者清除其内容然后复制可能不同的内容或什么?请更具体,并在您的帖子中添加更多详细信息。
  • 您的代码假定您需要逐行分隔数据,但为什么不利用 Excel 已经提供的功能呢?您可以将整个表格复制到每个工作表,然后过滤并删除每个工作表的隐藏。这可能会减少大量的工作量。
  • 是的,这就是我要说的。对于工作表所有部门 J 列中的每个唯一值,我最终会得到 4 张新工作表。所以对于上面的截图,我会以 4 个空白工作表结束。这 4 个空白工作表将被命名为“500 - NETWORK OPERATIONS”、“100 - CUSTOMER SERVICE”、“300 - ADMIN”和“700-ENGINEERING”。
  • 嘿西里尔,我认为该方法也可以工作并且会更快,但我仍然需要一种方法来动态创建工作表/将它们重命名为 J 列中的每个唯一值。跨度>

标签: excel vba autofilter


【解决方案1】:

试试这个。

Sub CreateSheets()

Dim rng As Range
Dim cl As Range
Dim dic As Object
Dim ky As Variant

    Set dic = CreateObject("Scripting.Dictionary")
    
    With Sheets("Sheet1")
        Set rng = .Range(.Range("J2"), .Range("J" & .Rows.Count).End(xlUp))
    End With
    
    For Each cl In rng
        If Not dic.exists(cl.Value) Then
            dic.Add cl.Value, cl.Value
        End If
    Next cl
    
    For Each ky In dic.keys
          Sheets.Add(After:=Sheets(Sheets.Count)).Name = dic(ky)
    Next ky
    
End Sub

【讨论】:

  • 正是我想要的。非常感谢!
  • 很高兴听到这个消息。
  • 您不需要参考,因为您使用的是late-bound 字典。如果您想利用创建的引用,即 early binding,您将应用以下更改:Dim dic As Scripting.DictionarySet dic = New Scripting.Dictionary
【解决方案2】:

创建标准工作表

  • 您的想法有问题,例如您使用 Hafiz Sb 的 CreateSheets 程序创建工作表,然后使用您的 CopyRows 程序写入数据。现在您将更多数据添加到主工作表中,但您被卡住了。您将如何将新数据添加到相应的工作表中?
  • 以下假设您只会添加,而不是从主工作表中删除数据。
  • 它将复制主工作表的次数与列 ('scCol') 中的唯一值一样多,并且通过使用 Autofilter,将删除每个工作表上不需要的数据(这是我的想法,但有些Cyril 在 cmets 中建议类似(如果不相同)。
  • 我做了类似here 的操作,它将工作表写入单独的工作簿。
Option Explicit

Sub CriteriaWorksheetsCreator()
    ' Accompanying procedures:
    '   ArrUniqueColumnRange
    '   DeleteWorksheetsViaArray
    
    Const sName As String = "All Dept."
    Const sFirst As String = "A1"
    Const sfRow As Long = 1 ' Header Row
    Const scCol As Long = 10 ' Criteria Column
    
    Dim wb As Workbook: Set wb = ThisWorkbook
    Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
    Dim rg As Range: Set rg = sws.Range(sFirst).CurrentRegion
    If rg.Rows.Count = 1 Then Exit Sub ' only one (header) row
    If rg.Columns.Count < scCol Then Exit Sub ' too few columns
    Dim strg As Range
    Set strg = rg.Resize(rg.Rows.Count - sfRow + 1).Offset(sfRow - 1)
    Dim sdrg As Range: Set sdrg = strg.Resize(strg.Rows.Count - 1).Offset(1)
    Dim scrg As Range: Set scrg = sdrg.Columns(scCol)
    
    Dim wsNames As Variant: wsNames = ArrUniqueColumnRange(scrg)
    If IsEmpty(wsNames) Then Exit Sub ' no valid data in 'scrg'
    
    Dim tAddress As String: tAddress = strg.Address
    Dim cAddress As String: cAddress = scrg.Address

    Application.ScreenUpdating = False
    
    DeleteWorksheetsViaArray wb, wsNames
    
    Dim dws As Worksheet
    Dim dtrg As Range
    Dim dcrg As Range
    Dim drg As Range
    Dim n As Long
    Dim dName As String
    For n = 0 To UBound(wsNames)
        sws.Copy After:=wb.Sheets(wb.Sheets.Count)
        Set dws = ActiveSheet
        dName = wsNames(n)
        dws.Name = dName
        Set dtrg = dws.Range(tAddress)
        dtrg.AutoFilter scCol, "<>" & dName
        If Application.Subtotal(103, dtrg.Columns(scCol)) > 1 Then
            Set dcrg = dws.Range(cAddress)
            Set drg = dcrg.SpecialCells(xlCellTypeVisible).EntireRow
            drg.Delete
        End If
        dws.AutoFilterMode = False
    Next n

    sws.Activate
    'wb.Save
    
    Application.ScreenUpdating = True

    MsgBox "Criteria worksheets created.", _
        vbInformation, "Criteria Worksheets Creator"

End Sub


''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose:      Returns the unique values from the first column of a range,
'               in an array.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function ArrUniqueColumnRange( _
    ByVal rg As Range) _
As Variant
    If rg Is Nothing Then Exit Function
    
    Dim Data As Variant
    Dim rCount As Long
    
    With rg.Columns(1)
        rCount = .Rows.Count
        If rCount = 1 Then
            ReDim Data(1 To 1, 1 To 1): Data(1, 1) = .Value
        Else
            Data = .Value
        End If
    End With
    
    With CreateObject("Scripting.Dictionary")
        .CompareMode = vbTextCompare
        Dim Key As Variant
        Dim r As Long
        For r = 1 To rCount
            Key = Data(r, 1)
            If Not IsError(Key) Then
                If Len(Key) > 0 Then
                    .Item(Key) = Empty
                End If
            End If
        Next r
        If .Count = 0 Then Exit Function ' only error values and/or blanks
        ArrUniqueColumnRange = .keys
    End With

End Function

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose:      Deletes all worksheets whose names are in an array ('wsNames'),
'               from a workbook ('wb').
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub DeleteWorksheetsViaArray( _
        ByVal wb As Workbook, _
        ByVal wsNames As Variant)
    On Error GoTo ClearError
    
    If wb Is Nothing Then Exit Sub
    
    Dim LB As Long: LB = LBound(wsNames)
    Dim UB As Long: UB = UBound(wsNames)
    Dim wsnCount As Long: wsnCount = UB - LB + 1
    
    Dim DeleteSheetNames() As String: ReDim DeleteSheetNames(0 To wsnCount - 1)
    
    Dim dn As Long
    
    Dim ws As Worksheet
    Dim sn As Long
    Dim wsName As String
    For sn = LB To UB
        wsName = wsNames(sn)
        On Error Resume Next
        Set ws = wb.Worksheets(wsName)
        On Error GoTo ClearError
        If Not ws Is Nothing Then
            If ws.Visible = xlSheetVeryHidden Then
                ws.Visible = xlSheetVisible
            End If
            DeleteSheetNames(dn) = wsName
            dn = dn + 1
            Set ws = Nothing
        End If
    Next sn
    
    If dn = 0 Then Exit Sub
    
    If dn < wsnCount Then
        ReDim Preserve DeleteSheetNames(0 To dn - 1)
    End If
    
    Application.DisplayAlerts = False
    wb.Worksheets(DeleteSheetNames).Delete
    Application.DisplayAlerts = True
        
ProcExit:
    Exit Sub
ClearError:
    Debug.Print "Run-time error '" & Err.Number & "': " & Err.Description
    Resume ProcExit
End Sub

【讨论】:

  • 您对将数据添加到主工作表的解释是正确的,但不需要。此报告用于库存,我们从不添加或删除工作表中的数据。我们组织中的每个项目都被捕获。这只是为了按部门分解信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-09
  • 2022-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-07
  • 1970-01-01
相关资源
最近更新 更多