在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
总结一下我的目标:
简单的答案是,代码不计算移动平均线,因为总和的代码不会从以前的行返回参考值。
首先回顾这段代码:
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)减去值。审查具体步骤可信的算法。