You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何改进VBA实现按规则拆分数据并按周一为起始周汇总

数据转换需求与VBA实现改进问询

示例数据

初始数据

Table 1                         Names
Type    Start       End         XXX     XYZ
A       2024-10-31  2024-11-13  48.25   47
B       2024-11-05  2024-11-14  0.75    80.5
C       2024-10-31  2024-10-31          5

目标结果

Table 2         
Type    Name    2024-10-28  2024-11-04  2024-11-11
A       XXX     9.75        23.75       14.75
A       XYZ     9.5         23.25       14.25
B       XXX     0           0.25        0.5
B       XYZ     0           40.25       40.25
C       XYZ     5           0           0

需求目标

这类似于unpivot(逆透视)操作:将数值按指定倍数拆分,先将基准倍数分配到所有工作日,剩余数值从起止日期向内分配,最后按周一为起始的周进行汇总。

注意:数值除以指定倍数后必为整数;
Table1位于其他工作簿的单元格区域;
Table2为本工作簿中的命名表格(已定义为ListObject);
当前暂不考虑节假日因素。

已梳理的步骤

  • 将数值除以工作日天数
  • 按指定倍数向下取整得到基准日值
  • 计算剩余数值
  • 确定需要分配额外数值的工作日数量
  • 将额外数值的工作日平均分配到起止日期,若数量为奇数则结束日期多分配1天
  • 为所有工作日分配基准日值
  • 为起始端的额外工作日分配倍数数值
  • 为结束端的额外工作日分配倍数数值
  • 按对应周汇总数值
  • 将结果填入目标表格中对应Type-Name组合与日期的交叉位置

现有代码

以下是正在开发的未完成代码,后半部分(888循环之后)需彻底重构:

Private Sub LoadPPT_Click()

Dim frm As PPT_Picker_Form
Dim wbPPT As Workbook
Dim wsPPT As Worksheet
Dim rngTopLeft As Range, rngBotRight As Range, rngPPTTable As Range
Dim lngLastUsedRow As Long
Dim lngLastCol As Long
Dim rngTargetCell As Range
Dim rngNameRow As Range
Dim rngNameCellFirst As Range, rngNameCellLast As Range
Dim lngTimeStart As Long
Dim rngStartDateCol As Range
Dim rngEndDateCol As Range
Dim rngHours As Range
Dim InputTable As ListObject
Dim lngInsertRow As Long
Dim lngPPTRowCounter As Long
Dim lngPPTColCounter As Long
Dim dStartDate As Date
Dim dEndDate As Date
Dim lngWeeks As Long
Dim dTaskSM As Date
Dim dTaskEM As Date
Dim lngTaskDays As Long
Dim dbTaskHoursPerDay As Double
Dim dbTaskDayUnits As Double
Dim dbTaskExtraUnitDays As Long
Dim lngDayCoutner As Long
Dim TestDate As Date
Dim dbWeek1Hours As Double
Dim dbWeeknHours As Double
Dim dbWeekLhours As Bouble



'Public strPPTFilepath As String
'Public strProjectNumber As String
    Set InputTable = Me.ListObjects("Forecast_Table")
    
    Set frm = UserForms.Add(PPT_Picker_Form.Name)
    frm.ListData = ThisWorkbook.Worksheets("Project Numbers").ListObjects("Project_Number_List").DataBodyRange
    
    frm.show
        
    If bCancelled Then
        Exit Sub
    End If
    
    'Define PPT Table
        
    Set wbPPT = Workbooks.Open(strPPTFilepath)
    Set wsPPT = wbPPT.Worksheets("Fee Estimate")
    
    'Find top left corner
    With wsPPT.Cells
        Set rngTopLeft = .Find("WBS Code", LookIn:=xlFormulas)
        
        If rngTopLeft Is Nothing Then
            'nothing found
            'add a manual cell picker routine
        End If
        
        Set rngTopLeft = rngTopLeft.Offset(2, 0)
    End With
    
    'Find last row
    lngLastUsedRow = wsPPT.Cells(wsPPT.Rows.count, rngTopLeft.Column).End(xlUp).Row
    
    'find Right edge
    With wsPPT.Cells
        Set rngTargetCell = .Find("Name", LookIn:=xlFormulas, LookAt:=xlWhole)
        'Assume first name is to the right of the cell containing "Name"
        Set rngNameCellFirst = rngTargetCell.Offset(0, 1)
                
        If rngTargetCell Is Nothing Then
            'nothing found
            'add a manual cell picker routine
        End If
        
        Do While rngTargetCell <> ""
            Set rngTargetCell = rngTargetCell.Cells.Offset(0, 1)
            lngLastCol = rngTargetCell.Column
        Loop
            
        Set rngNameCellLast = rngTargetCell.Cells.Offset(0, -1)
        lngLastCol = lngLastCol - 1
            
        Set rngTargetCell = .Find("Start Date", LookIn:=xlFormulas, LookAt:=xlWhole)
        Set rngStartDateCol = wsPPT.Range(rngTargetCell.Offset(2, 0), wsPPT.Cells(lngLastUsedRow, rngTargetCell.Column))
        
        Set rngTargetCell = .Find("End Date", LookIn:=xlFormulas, LookAt:=xlWhole)
        Set rngEndDateCol = wsPPT.Range(rngTargetCell.Offset(2, 0), wsPPT.Cells(lngLastUsedRow, rngTargetCell.Column))
        
        Set rngTargetCell = .Find("Units", LookIn:=xlFormulas, LookAt:=xlWhole)
        Set rngHours = wsPPT.Range(rngTargetCell.Offset(2, 0), wsPPT.Cells(lngLastUsedRow, rngNameCellLast.Column))
        
    End With
    
    'Define PPT table
    Set rngPPTTable = wsPPT.Range(rngTopLeft, wsPPT.Cells(lngLastUsedRow, lngLastCol))
    Set rngNameRow = wsPPT.Range(rngNameCellFirst, rngNameCellLast)
    
    InsertRow = TableLastUsedRow
    
   ' strProjectNumber
    If InputTable.ListRows(InputTable.ListRows.count).Range.Row = InsertRow Then
        InputTable.ListRows.Add AlwaysInsert:=True
        InsertRow = InsertRow + 1
    End If
    
    
        
        For lngPPTRowCounter = 1 To 888
            If WorksheetFunction.CountA(rngPPTTable.Row(lngPPTRowCounter)) <> 0 Then
                InputTable.ListColumns("Project No").ListRow(InsertRow) = strProjectNumber
                InputTable.ListColumns("Task No").ListRow(InsertRow) = rngPPTTable.Cells(PPTRowCounter, 2) & " - " & rngPPTTable.Cells(PPTRowCounter, 3)
                InputTable.ListColumns("Staff").ListRow(InsertRow) = rngNameRow(PPTColCounter)
                'Insert Funtcion to fill in Office Based on Name
                dStartDate = rngStartDate(rngPPTRowCounter)
                dEndDate = rngEndDate(rngPPTRowCounter)
                dTaskSM = Monday_Date(dStartDate)
                dTaskEM = Monday_Date(dEndDate)
                lngTaskDays = WorksheetFunction.NetworkDays(dStartDate, dEndDate)
                
                For lngPPTColCounter = 1 To 999
                    If rngHours(lngPPTRowCounter, lngPPTColCounter) > 0 Then
                        dbTaskHoursPerDay = WorksheetFunction.RoundDown((rngHours(lngPPTRowCounter, lngPPTColCounter) / lngTaskDays) / 0.25, 0) * 0.25
                        If dbTaskHoursPerDay = 0 Then
                            'Outside in fill to 0 routine
                        Else
                        dbTaskDayUnits = rngHours(lngPPTRowCounter, lngPPTColCounter) / dbTaskHoursPerDay
                        dbTaskExtraUnitDays = dbTaskDayUnits Mod lngTaskDays
                        
                        TestDate = dStartDate
                        
                        For lngDayCounter = 1 To (dEndate - dStartDate)
                            If Weekday(TestDate) > 1 And Weekday(TestDate) < 7 Then
                                If TestDate < dTaskSM + 5 Then
                                    Week1hours= Week1Hours+
                                ElseIf TestDate < dTaskEM - 2 Then
                                
                                Else
                                
                            End If
                        
                        
                        
                        Week1EndDate
                        Week2ndLastEndDate
                         
                        
                    End If
            End If
        
        
        Next lngPPTRowCounter
    Next lngPPTColCounter
    
End Sub

技术问询

如何改进当前的VBA实现方案,以完成从Table1到Table2的数据转换需求?

内容的提问来源于stack exchange,提问作者Forward Ed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 15:29:53