计算和更新加权移动平均预测数据使用ADO记录集

问题描述 投票:0回答:1

在Access 2016年,我试图计算出加权移动平均预测与结果更新我的预测表。

我的代码遍历记录没有任何问题,但我现在的逻辑不会计算加权平均值。因为它是,它只是返回每个时段的实际销售。我一直在调试这个了一段时间,不能换我的头周围。

我使用CRUD操作与SQL服务器的ADO类为通过ODBC我的后端。在下面我的代码,我已删除了错误处理压缩我的代码。如下:

Sub WMAForecast( _
                    lngCompanyID As Long, _
                    lngItemID As Long, _
                    dtmStartDate As Date, _
                    dtmEndDate As Date, _
                    intPeriods As Integer)                    

    ' Object related declarations ->
    Dim objRs As ADODB.Recordset
    Dim objDb1 As clADO
    Dim objDb2 As clADO
    Dim objEh As clError
    Dim strSQL1 As String: strSQL1 = vbNullString
    Dim strSQL2 As String: strSQL2 = vbNullString

    '// Generics variables ->
    Dim lngRecords As Long: lngRecords = 0
    Dim lngDetailsCount: lngDetailsCount = 0
    Dim lngDetailRecords: lngDetailRecords = 0
    Dim dblReturn As Double: dblReturn = 0
    Dim dblTempSum As Double: dblTempSum = 0
    Dim dblWeightSum As Double: dblWeightSum = 0

    '// Loop counters ->
    Dim i As Long: i = 0
    Dim j As Long: j = 0
    Dim k As Long: k = 0

    '// Calculate the sum of weights ->
    dblWeightSum = intPeriods * (intPeriods + 1) / 2

    '// Declare an array to store the weights ->
    Dim arrWeights As Variant
    ReDim arrWeights(1 To intPeriods)

    '// Construct SQL ->
    strSQL1 = "SELECT Sum(ItemDemandHistory.DemandUnits) AS Issues, PlanningCalendar.WeekEndDate, ItemDemandHistory.ItemID " & _
    "FROM PlanningCalendar INNER JOIN ItemDemandHistory ON PlanningCalendar.WeekEndDate = ItemDemandHistory.WeekEndDate " & _
    "GROUP BY PlanningCalendar.WeekEndDate, ItemDemandHistory.ItemID, PlanningCalendar.CompanyID " & _
    "HAVING PlanningCalendar.WeekEndDate>=? " & _
    "AND PlanningCalendar.WeekEndDate<=? " & _
    "AND ItemDemandHistory.ItemID=? " & _
    "AND PlanningCalendar.CompanyID=?"

    '// Validate parameters ->
    If Not fIsNullOrEmpty(strSQL1) And _
        Not fIsNullOrEmpty(lngCompanyID) And _
        Not fIsNullOrEmpty(lngItemID) And _
        Not fIsNullOrEmpty(dtmStartDate) And _
        Not fIsNullOrEmpty(dtmStartDate) And _
        Not fIsNullOrEmpty(intPeriods) Then

        '// Initialize database ->
        Set objDb1 = New clADO
        With objDb1
            .Initialize DatabaseType:=DBTypeEnum.TypeODBC
            .CursorLocation = adUseClient: .CommandType = adCmdText: .CursorType = adOpenStatic

            '// Retrieve recordset ->
            Set objRs = .ExecuteQuery(strSQL1, dtmStartDate, dtmEndDate, lngItemID, lngCompanyID)
            With objRs
                If Not (.EOF And .BOF) Then
                    If .RecordCount > 0 Then

                        '// Collect the number of records ->
                        lngRecords = .RecordCount

                        '// Construct and array to store the cummulative values ->
                        Dim arrCumulative As Variant
                        ReDim arrCumulative(1 To lngRecords) As Double

                        '// Construct and array to store the cummulative values ->
                        Dim arrWMA As Variant
                        ReDim arrWMA(1 To lngRecords) As Double

                        '// Move cursor to first position ->
                        .MoveFirst

                        '// Traverse through the recordset ->
                        For i = 1 To lngRecords

                            '// Set counter defaults ->
                            dblTempSum = 0
                            k = 0

                            '// Check if first record and assign first value to cummulative array ->
                            If i = 1 Then
                                arrCumulative(i) = .Fields(0)
                            Else
                                arrCumulative(i) = .Fields(0) + arrCumulative(i - 1)
                            End If

                            '// At points <= period N, calculate a simple average ->
                            '// Example using 3 Periods: If N=3, MA(1) = first series point, MA(2) = Average(first two points), MA(3) = Average(first three points)...etc ->
                            If i <= intPeriods Then
                                arrWMA(i) = arrCumulative(i) / i
                            Else
                                '// When i > intPeriods, the moving average calculation kicks in ->
                                For j = i - intPeriods + 1 To i
                                    k = k + 1
                                    dblTempSum = dblTempSum + .Fields(0) * k
                                Next j
                                arrWMA(i) = dblTempSum / dblWeightSum

                                '// Initialize database ->
                                Set objDb2 = New clADO
                                With objDb2
                                    .Initialize DatabaseType:=DBTypeEnum.TypeODBC: .CommandType = adCmdText

                                    '// Construct SQL ->
                                    strSQL2 = "UPDATE ItemDemandForecast " & _
                                    "SET ForecastUnits=? " & _
                                    "WHERE CompanyID=? " & _
                                    "AND ItemID=? " & _
                                    "AND WeekEndDate=?"

                                    '// Execute SQL ->
                                    lngDetailRecords = .ExecuteNonQuery(strSQL2, CDbl(arrWMA(i)), lngCompanyID, lngItemID, objRs.Fields(1))

                                    '// Increment record count ->
                                    lngDetailsCount = lngDetailsCount + lngDetailRecords
                                End With
                            End If
                        .MoveNext
                        Next
                    End If
                End If
            End With
        End With
    End If

    '// Cleanup ->
    Erase arrCumulative
    Erase arrWMA
    Erase arrWeights
    If Not objRs Is Nothing Then Set objRs = Nothing
    If Not objDb1 Is Nothing Then Set objDb1 = Nothing
    If Not objDb2 Is Nothing Then Set objDb2 = Nothing
    If Not objEh Is Nothing Then Set objEh = Nothing

End Function

下面是我的预计产出数据:

CompanyID   ItemID  Planning_Period Period_Ending   Demand_Units    Forecast_Units
1           10      1               2016-01-10      814             814
1           10      2               2016-01-17      1386            1386
1           10      3               2016-01-24      571             1100
1           10      4               2016-01-31      827             883.17
1           10      5               2016-02-07      1217            834.83
1           10      6               2016-02-14      1143            979.33
1           10      7               2016-02-21      1249            1115.00
1           10      8               2016-02-28      1303            1208.33
1           10      9               2016-03-06      1283            1258.33
1           10      10              2016-03-13      1379            1284.00
1           10      11              2016-03-20      990             1334.33
1           10      12              2016-03-27      1241            1168.50

总结一下我的目标:

  1. 计算基于以前的销售记录的项目/期加权移动平均预测。
  2. 使用计算出的预测,更新每个匹配项/周期的预测。
vba ms-access access-vba
1个回答
2
投票

简单的答案是,代码不计算移动平均线,因为总和的代码不会从以前的行返回参考值。


首先回顾这段代码:

dblWeightSum = intPeriods * (intPeriods + 1) / 2

这仅仅是一个整数从1到intPeriods总和,像1 + 2 + 3 + ... + intPeriods

现在去码

dblTempSum = 0
k = 0
...

For j = i - intPeriods + 1 To i
    k = k + 1
    dblTempSum = dblTempSum + .Fields(0) * k
Next j
arrWMA(i) = dblTempSum / dblWeightSum

首先,我们注意到,有加入和没有以前的值。换句话说,它不包括所有的前值。有以前的值没有引用。因此,这不可能是多行的运行平均值。

其次,考虑到循环迭代的总数是简单intPeriods。 k有效地从1开始,然后从1计数到intPeriods。循环的每次迭代乘以当前的k值相同的电流值.Fields(0)。总体而言,环路产生重写为下面的总和

dblTempSum = .Fields(0) * (1 + 2 + 3 + ... + intPeriods)

这是否很熟悉?它应该,因为它包含了相同的总和存储在dblWeightSum如前所述。

因此,从代码片断的最后线以上结束了减少像下面...

arrWMA(i) == dblTempSum / dblWeightSum
          == .Fields(0) * (1 + 2 + 3 + ... + intPeriods) / (1 + 2 + 3 + ... + intPeriods)
          == .Fields(0)

arrWMA(i)的价值是什么更新在随后的代码字段ForecastUnits。因此,用于移动平均场,而不是结束了与来自同一行原单值......就像你所观察到。


很抱歉,但我不能现在就发布正确的加权移动平均代码。然而,关键是要替换上面当前加权值减去前面的加权和的总和从片段中的电流回路。为了正确地做到这一点,我认为你需要至少一个以上的阵列,用于存储加权和,你需要从现有的总和,其超出的移动周期的大小(intPeriod)减去值。审查具体步骤可信的算法。

© www.soinside.com 2019 - 2024. All rights reserved.