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

Ms access 使用ADO记录集计算和更新加权移动平均预测数据,ms-access,vba,Ms Access,Vba,在Access 2016中,我尝试计算加权移动平均预测,并用结果更新我的预测表 我的代码在记录集中循环,没有任何问题,但我当前的逻辑不会计算加权平均值。实际上,它只是返回每个时期的实际销售额。我调试这个已经有一段时间了,我无法控制它 我使用ADO类进行CRUD操作,SQL server通过ODBC作为后端。在下面的代码中,我删除了错误处理以压缩代码。详情如下: Sub WMAForecast( _ lngCompanyID As Long, _

在Access 2016中,我尝试计算加权移动平均预测,并用结果更新我的预测表

我的代码在记录集中循环,没有任何问题,但我当前的逻辑不会计算加权平均值。实际上,它只是返回每个时期的实际销售额。我调试这个已经有一段时间了,我无法控制它

我使用ADO类进行CRUD操作,SQL server通过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
总结一下我的目标:

  • 根据以前的销售历史记录计算项目/期间的加权移动平均预测
  • 使用计算出的预测,更新每个匹配项目/期间的预测

  • 简单的回答是,代码并没有计算移动平均值,因为求和代码并没有引用前几行的值


    首先检查此代码:

    dblWeightSum = intPeriods * (intPeriods + 1) / 2
    
    这就是从1到整数周期的整数之和,比如
    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
    
    首先请注意,总和中没有以前的值。换句话说,它根本不包括以前的值。没有对以前的值的引用。因此,这不能是多行的运行平均值

    接下来,考虑循环迭代的<强>总数< /强>是简单的。代码>k有效地从1开始,然后从1向上计数。循环的每次迭代将相同的当前值

    .Fields(0)
    乘以当前k值。总的来说,循环生成一个重写的和,如下所示

    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)的值。查看可信算法的精确步骤。

    简单的答案是,代码没有计算移动平均值,因为求和代码没有引用前几行中的值


    首先检查此代码:

    dblWeightSum = intPeriods * (intPeriods + 1) / 2
    
    这就是从1到整数周期的整数之和,比如
    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
    
    首先请注意,总和中没有以前的值。换句话说,它根本不包括以前的值。没有对以前的值的引用。因此,这不能是多行的运行平均值

    接下来,考虑循环迭代的<强>总数< /强>是简单的。代码>k有效地从1开始,然后从1向上计数。循环的每次迭代将相同的当前值

    .Fields(0)
    乘以当前k值。总的来说,循环生成一个重写的和,如下所示

    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)的值。查看可信算法的精确步骤。

    应将源数据以文本格式发布为表格,以便读者可以轻松复制数据。该数据集没有运行该函数所需的数据(CompanyID、ItemID、StartDate)。“我无法让它按预期实际计算加权平均值”-它做了什么而不是您期望的?为什么有VBA和VB.net标记?应该是一个或另一个。@tamosa从代码注释、参数验证代码、错误检查(您说已删除)和其他清理代码中可以明显看出,您是彻底的,并且遵循良好的编码实践。为了记录在案,我在评论中非常直截了当,不要粗鲁,很明显,您知道如何很好地编写代码,所以我很惊讶您的问题没有提到所采取的任何调试步骤。@CPerkins-感谢您的大锤式反馈!我已经调试了好几个小时了,就是想都不敢想。也许这是我的逻辑,当涉及循环结构和序号时,我不是最好的程序员,还在学习,但这就是为什么我在这里,寻求帮助!应将源数据以文本格式发布为表,以便读者可以轻松复制数据。该数据集没有运行函数所需的数据(CompanyID、ItemID、StartDate)。“我无法获取