在C版的提醒下,补充一个效率提升的问题。
测试文件,每次测试都重新打开文件,不要保存:
表结构如下图:
此主题相关图片如下:spximage.jpg
![dvubb 图片点击可在新窗口打开查看](UploadFile/2010-12/201012910373267072.jpg)
希望在累计支出列计算出每个项目截止到当日为止的累计支出,有了上一次的经验,我想当然写出了这样这样的代码:
Dim s As Date = Date.Now()
Dim dic As New Dictionary(of DataRow, String)
Dim drs As List(of DataRow)
For Each dr1 As DataRow In DataTables("表A").DataRows
Dim Filter As String
Filter = "项目 = '" & dr1("项目") & "' And 日期 <= #" & dr1("日期") & "#"
dic.Add(dr1, DataTables("表A").Compute("Sum(支出)",Filter))
Next
For Each dr As DataRow In dic.Keys
dr("累计支出") = dic(dr)
Next
Output.show(“执行时间:" & (Date.Now -s).TotalSeconds)
在一万行,累计支出列无内容的情况下,花了390秒。
这样的结果不仅让人难以接受,也出乎意料,为什么之前的招数完全失效了呢?
仔细分析一下可以看出,和之前的例子不同,上面的查询条件不全是“等于”比较,其中日期的比较是“小于等于”比较。
实际上在查询中,“等于”比较是最高效的,其他任何形式的比较效率都要低很多,也许真的就是这个“小于等于”比较影响了代码的执行效率。
但是“小于等于”比较是必须,不可能不用,那么我们唯一能着手的就是缩小“小于等于”比较的范围,改良后的代码为:
Dim s As Date = Date.Now()
Dim dic As New Dictionary(of DataRow, String)
Dim drs As List(of DataRow)
For Each dr1 As DataRow In DataTables("表A").DataRows
Dim dv As Date = dr1("日期")
Dim sm As Double = 0
drs = DataTables("表A").Select("项目 = '" & dr1("项目") & "'", "日期")
For Each dr2 As DataRow In drs
If dr2("日期") <= dv Then
sm = sm + dr2("支出")
Else
Exit For
End If
Next
dic.Add(dr1, sm)
Next
For Each dr As DataRow In dic.Keys
dr("累计支出") = dic(dr)
Next
Output.show("执行时间" & (Date.Now -s).TotalSeconds)
同样在一万行,累计支出列无内容的情况下,上述代码花了2秒,看来之前的判断完全正确。
代码的原理是,先筛选出相同项目的行,再比较这些行的日期,从而大大缩小了日期“小于等于”比较的范围,如果你的项目是100个,那么比较范围就缩小了100倍,从而大大提升了代码的运行效率。
既然比较是影响效率的关键,于是我彻底改写代码,完全不用比较,最新的代码为:
Dim s As Date = Date.Now()
Dim drs As List(of DataRow) = DataTables("表A").Select("", "项目, 日期")
drs(0)("累计支出") = drs(0)("支出")
For i As Integer = 1 To drs.Count - 1
If drs(i)("项目") = drs(i - 1)("项目") Then
drs(i)("累计支出") = drs(i-1)("累计支出") + drs(i)("支出")
Else
drs(i)("累计支出") = drs(i)("支出")
End If
Next
Output.show("执行时间:" & (Date.Now -s).TotalSeconds)
经过测试,处理1万行数据只用了0.9秒,又提升了一倍有余,而且代码更为简洁易懂。
代码的效率提升是没有定式的,实际编码的过程中只有用排除法,找出可能影响效率的因素,然后有针对性地改进。
[此贴子已经被作者于2010-12-9 14:09:28编辑过]