在Access 2016年,我试图计算出加权移动平均预测与结果更新我的预测表。
我的代码遍历记录集没有任何问题,但是我当前的逻辑不会计算加权平均值。实际上,它只是返回每个期间的实际销售额。我一直在调试这个了一段时间,不能换我的头周围。
我将ADO类用于SQL Server的CRUD操作,作为我通过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
总结我的目标:
答案 0 :(得分: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。循环的每次迭代都将相同的当前值 .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)的值。查看可信赖的算法以获取准确的步骤。