以文本方式查看主题

-  Foxtable(狐表)  (http://www.foxtable.com/bbs/index.asp)
--  专家坐堂  (http://www.foxtable.com/bbs/list.asp?boardid=2)
----  令人目瞪口呆的效率差别(有修正)  (http://www.foxtable.com/bbs/dispbbs.asp?boardid=2&id=8781)

--  作者:狐狸爸爸
--  发布时间:2010/12/7 12:36:00
--  令人目瞪口呆的效率差别(有修正)

今天一个用户,希望在第三列中标出第一列有而第二列无的数据,假定第一列和第二列为整数型:

 


此主题相关图片如下:1502.gif
按此在新窗口浏览图片

 

测试文件,注意每次测试都重新打开项目,不要保存:

 

 

 下载信息  [文件大小:   下载次数: ]
点击浏览该文件:管理项目22.table

 


我给出的代码为:

 

For Each dr As DataRow In DataTables("表A").DataRows
    If
DataTables("表A").Find("第二列 = " & dr("第一列") ) Is Nothing Then
        dr("第三列") =
True
    Else

        dr("第三列") =
False
    End
If
Next

 

在数据为1万行,第三列无数据的时候,执行上述代码花费了整整234秒,显然这样的效率是让人难以接受的。
上面的代码并没有什么特别的地方,在遍历过程中找出符合条件的行,并给此行的某列赋值而已。
那么到底是查找出老问题,还是赋值出了问题,以至于效率如此之低呢?
为了找出原因,我分别测试了查询和赋值:

 

测试查询的代码:

 

Dim v As Boolean
For Each
dr As DataRow In DataTables("表A").DataRows
    If DataTables(
"表A").Find("第二列 = " & dr("第一列")) Is Nothing Then
        v = dr(
"第三列")
    End If
Next

 

测试赋值的代码:

 

For Each dr As DataRow In DataTables("表A").DataRows
    dr
("第三列"
) = True
Next

 

出人意料的是,两段代码运行得都非常快,都在瞬间完成了。
那为什么第一段代码需要花费234秒?难道查询和赋值不能同时出现在同一个遍历语句中吗?
为此我改写了第一段代码,让查询和赋值分开进行,新的代码为:

 

Dim lst1 As New List(of DataRow)
Dim
lst2 AS New List(of DataRow)
For Each
dr As DataRow In DataTables("表A").DataRows
    If DataTables(
"表A").Find("第二列 = " & dr("第一列")) Is Nothing Then
        lst1
.Add(dr)
    Else

        lst2
.Add(dr)
    End If
Next
For Each
dr As DataRow In lst1
    dr
("第三列") = True
Next
For Each
dr As DataRow In lst2
    dr
("第三列") = False
Next

 

同样在数据为1万行,第三列无数据的时候,执行上述代码只花了0.8秒,比原来的234秒快了整整300倍,效率差距之大,让人目瞪口呆。

原因应该找到了,为了验证我的想法,我另外写了两段代码测试,这次不使用Find进行查询,而是用Compute进行计算:

 

常规的代码,计算和赋值在同一个遍历语句:

 

Dim v As Double
For Each
dr As DataRow In DataTables("表A").DataRows
    v
= DataTables("表A").Compute("Count(第二列 )", "第二列 = " & dr("第一列"))
   
dr("第四列") = v

Next

 

估计应该更加高效的代码,计算和赋值分开进行:

 

Dim Dic As new Dictionary(of DataRow, Integer)
Dim
v As Double
For Each
dr As DataRow In DataTables("表A").DataRows
    v = DataTables("表A").Compute("Count(第二列 )", "第二列 = " & dr("第一列"))
    dic
.Add(dr, v
)
Next
For Each
dr As DataRow In dic.Keys
   
dr("第四列") = dic(dr)
Next

 

果不其然,经过测试,第二段比第一段快了整整300倍。

 

现在我总结一下,符合以下三个条件,可能会出现效率低下的情况:

 

1、用For语句遍历某个表
2、遍历过程中用Find或Select查询此表,或者用Compute方法统计体表;
3、遍历过程中会修改此表中某些列的值。

 

强调一下,上述三项中提到的表必须都是同一个表。

我们改写的代码之所有高效,是因为改写后的代码2、3不再出现在同一个遍历语句中,而是在不同的遍历语句中出现。

 

 

[此贴子已经被作者于2010-12-7 23:31:50编辑过]

--  作者:kylin
--  发布时间:2010/12/7 13:23:00
--  

这个鱼和熊掌不可得兼的经验,收藏!


--  作者:易服
--  发布时间:2010/12/7 13:41:00
--  

有一按钮“编号”:自觉效率不高,能优化?

Dim str As String
For Each dr As DataRow In DataTables("申请表").DataRows
    str = "[乡名] = \'" & dr("乡名") & "\'And [村名] = \'" & dr("村名") & "\' And [组别] = \'" & dr("组别") & "\'"
    str = str  & "And  [单位(个人)] = \'" & dr("单位(个人)") & "\'  And  [法定代表人] = \'" & dr("法定代表人") & "\' And [_Identify] < " & dr("_Identify")
    dr("宗地序号") = Format(Tables("申请表").Compute("Count(乡名)",str)+1,"00")
Next
Tables("申请表").Sort = "法定代表人 DESC" \'降序排列


--  作者:狐哥
--  发布时间:2010/12/7 14:05:00
--  

添加到集合再对比,真是高。受用了。


--  作者:migold
--  发布时间:2010/12/7 14:25:00
--  

顶!试试去


--  作者:程兴刚
--  发布时间:2010/12/7 14:33:00
--  
收藏慢慢消化!
--  作者:狐狸爸爸
--  发布时间:2010/12/7 14:35:00
--  
以下是引用易服在2010-12-7 13:41:00的发言:

有一按钮“编号”:自觉效率不高,能优化?

Dim str As String
For Each dr As DataRow In DataTables("申请表").DataRows
    str = "[乡名] = \'" & dr("乡名") & "\'And [村名] = \'" & dr("村名") & "\' And [组别] = \'" & dr("组别") & "\'"
    str = str  & "And  [单位(个人)] = \'" & dr("单位(个人)") & "\'  And  [法定代表人] = \'" & dr("法定代表人") & "\' And [_Identify] < " & dr("_Identify")
    dr("宗地序号") = Format(Tables("申请表").Compute("Count(乡名)",str)+1,"00")
Next
Tables("申请表").Sort = "法定代表人 DESC" \'降序排列

 

可以这么改改看看:

 

Dim str As String
Dim Dic As New Dictionary(of DataRow, String)
For Each dr As DataRow In DataTables("申请表").DataRows
    str = "[乡名] = \'" & dr("乡名") & "\'And [村名] = \'" & dr("村名") & "\' And [组别] = \'" & dr("组别") & "\'"
    str = str  & "And  [单位(个人)] = \'" & dr("单位(个人)") & "\'  And  [法定代表人] = \'" & dr("法定代表人") & "\' And [_Identify] < " & dr("_Identify")
    dic.Add(dr,Format(DataTables("申请表").Compute("Count(乡名)",str)+1,"00"))
Next
For dr As DataRow In dic.Keys
    dr("宗地序号") = dic(dr)
Next


--  作者:易服
--  发布时间:2010/12/7 14:43:00
--  
以下是引用狐狸爸爸在2010-12-7 14:35:00的发言:

 

可以这么改改看看:

 

Dim str As String
Dim Dic As New Dictionary(of DataRow, String)
For Each dr As DataRow In DataTables("申请表").DataRows
    str = "[乡名] = \'" & dr("乡名") & "\'And [村名] = \'" & dr("村名") & "\' And [组别] = \'" & dr("组别") & "\'"
    str = str  & "And  [单位(个人)] = \'" & dr("单位(个人)") & "\'  And  [法定代表人] = \'" & dr("法定代表人") & "\' And [_Identify] < " & dr("_Identify")
    dic.Add(dr,Format(DataTables("申请表").Compute("Count(乡名)",str)+1,"00"))
Next
For dr As DataRow In dic.Keys
    dr("宗地序号") = dic(dr)
Next


图片点击可在新窗口打开查看此主题相关图片如下:2010-12-7 14-42-11.png
图片点击可在新窗口打开查看

--  作者:狐狸爸爸
--  发布时间:2010/12/7 15:02:00
--  

这都看不出来吗,我手误而已, 应该是:

 

For Each dr As DataRow In dic.keys

 


--  作者:狐狸爸爸
--  发布时间:2010/12/7 15:04:00
--  

看来不单单查询这样,遍历过程中,任何涉及到所有行的操作,例如查找和计算都如此,我将第一列和第二列改为整数型,下面两段代码效率同样相差几百倍:

 

普通的:

Dim v As Double
For Each dr As DataRow In DataTables("表A").DataRows
    v = DataTables("表A").Compute("Sum(第二列 )", "第二列 = " & dr("第一列"))
    dr("第四列") = v
Next

 

高效的,快了300倍:

Dim Dic As new Dictionary(of DataRow, Integer)
Dim v As Double
For Each dr As DataRow In DataTables("表A").DataRows
    v = DataTables("表A").Compute("Sum(第二列 )", "第二列 = " & dr("第一列"))
    dic.Add(dr, v) 
Next
For Each dr As DataRow In dic.Keys
    dr("第四列") = dic(dr)
Next