非等于比较

在学习本节内容之前,请先打开CaseStudy目录下的文件:代码优化.Table

这次问题的表结构如下:

要求在累计支出列计算出每个项目截止到当日为止的累计支出,标准的代码是:

Dim st 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("日期") & "#"
   
dr1("累计支出") = DataTables("表A").Compute("Sum(支出)",Filter)
Next

MessageBox
.Show("耗时: " & (Date.Now - st).TotalSeconds & "秒")

在一万行数据的情况下,执行上述代码花了390秒,这完全在意料之中。

有了示例一的经验,我信心十足地给出了“优化”后的代码:

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

可出乎意料的是,在一万行数据的情况下,执行上述代码同样花了390秒,效率竟然没有丝毫的提升。

为什么之前的招数失效了呢?

仔细分析一下可以看出,和之前的例子不同,上面的查询条件不全是“等于”比较,其中日期的比较是“小于等于”比较。
实际上在查询中,“等于”比较是最高效的,其他任何形式的比较效率都要低很多,也许真的就是这个“小于等于”比较影响了代码的执行效率。
但是“小于等于”比较是必须,不可能不用,我们唯一能着手的就是减少“小于等于”比较的次数,改良后的代码为:

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

同样在一万行数据的情况下,执行上述代码花了2秒,看来之前的判断完全正确。
代码的原理是,先筛选出相同项目的行,再比较这些行的日期,从而大大较少了“小于等于”比较的次数,如果你的项目是100个,那么比较次数就减少了100倍,运行效率也就提高了100倍。

既然查询中的非等于比较是影响效率的关键,于是我索性彻底改写代码,连查询也不用了,最新的代码为:

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

经过测试,处理1万行数据只用了0.9秒,效率又提升了一倍有余,而且代码更为简洁易懂。

从390秒到2秒,再从2秒到0.9秒,可见代码的优化对于效率的提升是何其重要。

代码效率的提升是没有定式的,实际编码的过程中只有运用排除法,找出可能影响效率的因素,然后有针对性地优化改进。


本页地址:http://www.foxtable.com/webhelp/topics/2219.htm