Foxtable(狐表)用户栏目专家坐堂 → 令人目瞪口呆的效率差别(有修正)


  共有47197人关注过本帖树形打印复制链接

主题:令人目瞪口呆的效率差别(有修正)

帅哥哟,离线,有人找我吗?
狐狸爸爸
  1楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:管理员 帖子:47497 积分:251403 威望:0 精华:91 注册:2008/6/17 17:14:00
令人目瞪口呆的效率差别(有修正)  发帖心情 Post By: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编辑过]

[本帖被加为精华]
 回到顶部
帅哥哟,离线,有人找我吗?
狐狸爸爸
  2楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:管理员 帖子:47497 积分:251403 威望:0 精华:91 注册:2008/6/17 17:14:00
  发帖心情 Post By: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


 回到顶部
帅哥哟,离线,有人找我吗?
狐狸爸爸
  3楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:管理员 帖子:47497 积分:251403 威望:0 精华:91 注册:2008/6/17 17:14:00
  发帖心情 Post By:2010/12/7 15:02:00 [显示全部帖子]

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

 

For Each dr As DataRow In dic.keys

 


 回到顶部
帅哥哟,离线,有人找我吗?
狐狸爸爸
  4楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:管理员 帖子:47497 积分:251403 威望:0 精华:91 注册:2008/6/17 17:14:00
  发帖心情 Post By: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


 回到顶部
帅哥哟,离线,有人找我吗?
狐狸爸爸
  5楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:管理员 帖子:47497 积分:251403 威望:0 精华:91 注册:2008/6/17 17:14:00
  发帖心情 Post By:2010/12/7 16:13:00 [显示全部帖子]

以下是引用易服在2010-12-7 15:59:00的发言:

两者几乎没什么差别,12740条记录用时原来代码1分48秒多,新代码1分46多,手按秒表计时有点误差。

 

有差别的,你改得不对而已。


 回到顶部
帅哥哟,离线,有人找我吗?
狐狸爸爸
  6楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:管理员 帖子:47497 积分:251403 威望:0 精华:91 注册:2008/6/17 17:14:00
  发帖心情 Post By:2010/12/7 17:14:00 [显示全部帖子]

下面这个文件,可以配合一楼的代码测试,注意每次测试都重新打开项目,不用保存:

 

 下载信息  [文件大小:   下载次数: ]
图片点击可在新窗口打开查看点击浏览该文件:管理项目22.table

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

 回到顶部
帅哥哟,离线,有人找我吗?
狐狸爸爸
  7楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:管理员 帖子:47497 积分:251403 威望:0 精华:91 注册:2008/6/17 17:14:00
  发帖心情 Post By:2010/12/7 22:09:00 [显示全部帖子]

Dictionary帮助中有的:

 

http://help.foxtable.com/topics/1945.htm

 


 回到顶部
帅哥哟,离线,有人找我吗?
狐狸爸爸
  8楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:管理员 帖子:47497 积分:251403 威望:0 精华:91 注册:2008/6/17 17:14:00
  发帖心情 Post By:2010/12/7 22:58:00 [显示全部帖子]

呵呵,其实真的没啥用的,一楼的问题,一般人也遇不到。

 


 回到顶部
帅哥哟,离线,有人找我吗?
狐狸爸爸
  9楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:管理员 帖子:47497 积分:251403 威望:0 精华:91 注册:2008/6/17 17:14:00
  发帖心情 Post By:2010/12/7 23:13:00 [显示全部帖子]

以下是引用hnaysx在2010-12-7 22:59:00的发言:
关键我这里测试不出来你所的200多秒

 

那就比较一下这两段吧

 

第一段:

 

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


 回到顶部
帅哥哟,离线,有人找我吗?
狐狸爸爸
  10楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:管理员 帖子:47497 积分:251403 威望:0 精华:91 注册:2008/6/17 17:14:00
  发帖心情 Post By:2010/12/7 23:53:00 [显示全部帖子]

其实这是ado.net的一个bug或不足,下面两段代码,和Foxtable没有关系,第二段还是比第一段快上百倍:

 

第一段:

 

Dim t As New Data.DataTable
Dim r As Data.DataRow
t.Columns.Add("第一列",Gettype(Integer))
t.Columns.Add("第二列",Gettype(Integer))
t.Columns.Add("第三列",Gettype(Boolean))
For i As Integer = 0 To 10000
    r = t.NewRow()
    r("第一列") = rand.Next(1,30000)
    r("第二列") = rand.Next(1,30000)
    t.Rows.Add(r)
Next
Dim tm As Date = Date.Now
For Each r In t.Rows
    If t.Select("第二列 = " & r("第一列")).Length > 0 Then
        r("第三列") = True
    Else
        r("第三列") = False
    End If
Next
output.show((Date.Now - tm).TotalSeconds)


 

第二段:

 

Dim t As New Data.DataTable
Dim r As Data.DataRow
Dim l1 As New List(of Data.DataRow)
Dim l2 As New List(of Data.DataRow)
t.Columns.Add("第一列",Gettype(Integer))
t.Columns.Add("第二列",Gettype(Integer))
t.Columns.Add("第三列",Gettype(Boolean))
For i As Integer = 0 To 10000
    r = t.NewRow()
    r("第一列") = rand.Next(1,30000)
    r("第二列") = rand.Next(1,30000)
    t.Rows.Add(r)
Next
Dim tm As Date = Date.Now
For Each r In t.Rows
    If t.Select("第二列 = " & r("第一列")).Length > 0 Then
        l1.Add(r)     
    Else
        l2.Add(r)
    End If
Next
For Each r In l1
    r("第三列") = True
Next
For Each r In l2
    r("第三列") = False
Next
output.show((Date.Now - tm).TotalSeconds)

 

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

 回到顶部
总数 32 1 2 3 4 下一页