ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 Excel Home精品图文教程库
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
300集Office 2010微视频教程 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
楼主: zhaogang1960

[Excel 程序开发] 字段不完全相同的多个工作簿按工作表名汇总[已小结]

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2012-1-30 23:07 | 显示全部楼层
zhaogang1960 发表于 2012-1-30 17:01
我能拿出来的“证据”也只能打印区域这个假工作表了,其实从某些数据库中导出来的工作簿也可能含有类似的 ...

原来工作簿里定义的名称也被认为是table表了。谢谢赵老师答疑解惑。

TA的精华主题

TA的得分主题

发表于 2012-1-31 12:38 | 显示全部楼层
本想趁春节假期好好做一下这个很实用的题目,结果瞎忙得比上班时间还厉害,等到截止日期时,才发现这个题目比我想象中难多了,折腾了半天也没作出来.我以前认为实际应用中需要的excel知识都不会太难,这次的题目彻底改变了我的这个观念.
赵老师对数组和字典的互相转化和弥补真是运用到了极致,实在佩服极了.这次的竞赛题是对彭版这个帖子(http://club.excelhome.net/thread-528057-1-1.html)的全面回答和提升.
谢谢赵老师,每次题目对我来说都让我学到了很多

点评

我人懒看书看帖都头疼,还有这样巧合的事儿,得好好拜读彭版的大作  发表于 2012-1-31 12:48

TA的精华主题

TA的得分主题

发表于 2012-1-31 13:10 | 显示全部楼层
zhaogang1960 发表于 2012-1-29 13:17
小结:

呵呵,彭版那个题目只是在"列字段不同"和本期竞赛题类似,其它还是很不相同的,所以个人觉得这次是"全面回答和提升".否则我也能捡个便宜,勉力完成本期竞赛题哈:)

TA的精华主题

TA的得分主题

发表于 2012-1-31 21:44 | 显示全部楼层
本帖最后由 xmyjk 于 2012-2-1 16:38 编辑
wcymiss 发表于 2012-1-29 22:29
原来,除了cat.tables的table.name可以取得表名,cnn.OpenSchema也可以取表名啊。而且速度比cat.tabl ...

仅针对取表名和字段名的方法进行讨论,集合了42楼赵老师的意见再次又整理了下:

呵呵,我昨晚翻查了一晚的VB相关论坛和ADO的对象,发现现在几个论坛主流的运用ADO或ADOX去获取表名以及字段名的做法有些片面,现在也和大家分享下我整理的,也请各位指教。

1.关于使用ADO对象的.OpenSchema方法获取表名和字段名
    几个主流论坛,关于使用.OpenSchema方法获取表名和字段名的做法是,先运用.openschema(adSchemaTables),获取表名,然后进行.Execute执行SQL语句,获取Recordset,然后读取FIELDS,其实不然在,如下图,
,.openschema(adSchemaColumns)就可以获取表名以及字段名,视乎没必要用.EXECUTE。整体代码如下,可以测试附件,附件的运行为了区分度,都是让汇总程序执行了20次,运行结果体现,速度还是不错的:

[code=vb]
Sub OPENCL()
    Dim d As New Dictionary, i%
    Dim myFile As String, mypath As String, bm As String
    Dim cnn As ADODB.Connection
    Dim rst1 As ADODB.Recordset

    mypath = ThisWorkbook.Path & "\数据源\"
    Application.ScreenUpdating = False

    myFile = Dir(ThisWorkbook.Path & "\数据源\*.xls")
    Do While myFile <> ""
        Set cnn = New ADODB.Connection
        cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties=Excel 8.0;Data Source=" & mypath & myFile
        Set rst1 = cnn.openschema(adSchemaColumns)
        With rst1
            Do While Not .EOF
                bm = rst1!table_name
                If d.Exists(bm) = False Then
                    Set d(bm) = New Dictionary
                End If
                d(bm)(CStr(rst1!COLUMN_NAME)) = 0
                .MoveNext
            Loop
        End With
        myFile = Dir
    Loop
    Dim arr
    arr = d.Keys
    For i = 0 To UBound(arr)
        'Debug.Print arr(i), Join(d(arr(i)).Keys, ","), d(arr(i)).Count
    Next
    Erase arr
    Application.ScreenUpdating = True
    Set d = Nothing
    rst1.Close
    Set rst1 = Nothing
    cnn.Close
    Set cnn = Nothing
End Sub
[/code]

2.关于使用ADOX获取表名和字段名
     几个主流论坛,都是使用ADOX进行.ActiveConnection,然后从.Tables对象集合里面去获取表名,然后再去做ADO的.Connection,然后就.exectue去获取字段名。请大家看下图。
,其实,用ADOX进行连接后,获取的.tables对象集合里面,已经包含了.columns的对象集合,这个集合是什么呢,就是我们需要的字段名。所以没必要再次建立ADO连接,然后执行sql语句去获取字段名。测试了下结果,速度上和原来execute不想上下,估计是.columns的对象集合也比较庞大,调用起来比较耗费效率。
整体代码如下:
[code=vb]
Sub ADOXTB()
    Dim cat As adox.Catalog, i%
    Dim d As New Dictionary
    Dim myFile As String, mypath As String, bm As String
    Dim tb As Table, cl As Column

    mypath = ThisWorkbook.Path & "\数据源\"

    Application.ScreenUpdating = False

    myFile = Dir(ThisWorkbook.Path & "\数据源\*.xls")
    Do While myFile <> ""
        Set cat = New adox.Catalog
        cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties=Excel 8.0;Data Source=" & mypath & myFile
        For Each tb In cat.Tables
            With tb
                bm = CStr(.Name)
                If d.Exists(bm) = False Then
                    Set d(bm) = New Dictionary
                End If
                For Each cl In .Columns
                    d(bm)(cl.Name) = 0
                Next
            End With
        Next
        myFile = Dir
    Loop
    Dim arr
    arr = d.Keys
    For i = 0 To UBound(arr)
        'Debug.Print arr(i), Join(d(arr(i)).Keys, ","), d(arr(i)).Count
    Next
    Set cat = Nothing
    Set d = Nothing
    Erase arr
    Application.ScreenUpdating = True
End Sub
[/code]

3.如果从单纯获取表名的方法来对比
    两种方法都能取到表名。且效率都很高,但是,用.openschema(adSchemaTables)的好处就是,后面再执行SQL语句的时候,不用再次连接,因为CNN.OPEN已经做好了,可以直接使用了。
    [code=vb]
Sub adox()
    Dim cat As adox.Catalog
    Dim d As New Dictionary
    Dim myFile As String, mypath As String
    Dim tb As Table

    mypath = ThisWorkbook.Path & "\数据源\"

    Application.ScreenUpdating = False

    myFile = Dir(ThisWorkbook.Path & "\数据源\*.xls")
    Do While myFile <> ""
        Set cat = New adox.Catalog
        cat.ActiveConnection = "Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties=Excel 8.0;Data Source=" & mypath & myFile
        For Each tb In cat.Tables
            d(CStr(tb.Name)) = 0
        Next

        Set tb = Nothing
        myFile = Dir
    Loop
    'Debug.Print Join(d.Keys, ",")
    Set d = Nothing
    Set cat = Nothing
    Application.ScreenUpdating = True
End Sub

Sub openschema()
    Dim d As New Dictionary
    Dim myFile As String, mypath As String
    Dim cnn As ADODB.Connection
    Dim rst1 As ADODB.Recordset

    mypath = ThisWorkbook.Path & "\数据源\"
    Application.ScreenUpdating = False

    myFile = Dir(ThisWorkbook.Path & "\数据源\*.xls")
    Do While myFile <> ""
        Set cnn = New ADODB.Connection
        cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties=Excel 8.0;Data Source=" & mypath & myFile
        Set rst1 = cnn.openschema(adSchemaTables)
        With rst1
            Do While Not .EOF
                d(CStr(rst1!table_name)) = 0
                .MoveNext
            Loop
        End With
        myFile = Dir
    Loop
    'Debug.Print Join(d.Keys, ",")
    rst1.Close
    cnn.Close
    Set rst1 = Nothing
    Set cnn = Nothing
    Application.ScreenUpdating = True
    Set d = Nothing
End Sub
    [/code]

    附件上传了,大家都可以试看看,四个方式的比较。

    最后,第1点和第2点的取表头方式,都和传统的FIELDS集合中取的表头的顺序不同(即与原表表头不同),因为系统有进行了排序,因此,实践操作中,可以先把关键字段放入字典,后续循环的时候,排除该关键字即可,如果是很乱序的多表表头,应该不会有什么影响。

    还是比较推荐用.openschema(adSchemaColumns)方式取获取不同字段多表表头的,效率很高,且与表的链接已经做好了,下一步如果要执行什么SQL代码,也可以直接用。

    总之,存在即合理,微软设计了ADOX对象来针对数据结构的操作,肯定是有独到的地方的,虽然表现出来ADOX的效率貌似那么不尽人意,但是他还有很多其他功能值得我们去探究的。

    VBA的世界真的很广,随便研究下都能研究出很多的。呵呵呵。最后上传一下找到的一个ADO和ADOX对象的完全手册,有兴趣的同志,也可以下载研究下。

    对了,最最最后,分享下ADO一个学习资料的网址,里面还有很多方法事件对象的示例。http://doc.51windows.net/ado/?url=/ado/mdmthopenschema.htm。另外只是方法讨论,代码还没考虑楼楼上那些表名判断的问题。
   
    最后,请各位高手多多指正。谢谢。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?免费注册

x

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-1-31 22:24 | 显示全部楼层
xmyjk 发表于 2012-1-31 21:44
呵呵,我一开始也以为.OpenSchema更快,其实不然,我昨晚翻查了一晚的VB相关论坛和ADO的对象,发现现在几 ...

这个方法我试过,好像取得字段名顺序和工作表不太一致,请对比一下

点评

是不一样,测试过了,呃,不过如果是乱序的表表头,表头有时候也不是那么整齐的,可以先把关键表头放入字典,然后后面循环剔除它就行了。  发表于 2012-2-1 15:11

TA的精华主题

TA的得分主题

发表于 2012-1-31 22:25 | 显示全部楼层
本帖最后由 wcymiss 于 2012-1-31 22:28 编辑
xmyjk 发表于 2012-1-31 21:44
呵呵,我一开始也以为.OpenSchema更快,其实不然,我昨晚翻查了一晚的VB相关论坛和ADO的对象,发现现在几 ...


      虽然用adox和openschema方法都可以取字段名,可是取出的字段名是自动排了序的,不是数据源原来的字段顺序,如果接下来再用getrows方法的话,字段名和数组就对应不上了。所以我还是选择execute再field来取字段名。
      单纯取表名的话,我测试下来openschema比adox略快些。
      access的ado帮助大部分都看不懂。等以后有了实例可能会看得懂些。
     另:弦月你怎么老是搭末班车啊{:soso_e120:}?

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2012-1-31 22:32 | 显示全部楼层
本帖最后由 xmyjk 于 2012-2-1 01:16 编辑
wcymiss 发表于 2012-1-31 22:25
虽然用adox和openschema方法都可以取字段名,可是取出的字段名是自动排了序的,不是数据源原来的字 ...

getrows方法是好用,这个俺和小胖都一直在学习你的代码,嘻嘻嘻,吴姐厉害大大滴。

这那一段仅仅针对去表名以及字段这一个环节的方法的探讨。

呃,我这里是XP+2010,出来效果如图。赵老师那边是谁快呢?


没办法啊,家里忙装修什么的,工作又忙。。。这阶段忙得晕头转向的。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?免费注册

x

TA的精华主题

TA的得分主题

 楼主| 发表于 2012-1-31 22:44 | 显示全部楼层
xmyjk 发表于 2012-1-31 22:32
getrows方法是好用,这个俺和小胖都一直在学习你的代码,嘻嘻嘻,吴姐厉害大大滴。

这那一段仅仅针对去 ...

我这里测试也是这样,还没有细看
有收获,本来要求速度目的是为了避免抄袭论坛上已经有的,两次打开文件的方法

TA的精华主题

TA的得分主题

发表于 2012-1-31 23:22 | 显示全部楼层
xmyjk 发表于 2012-1-31 22:32
getrows方法是好用,这个俺和小胖都一直在学习你的代码,嘻嘻嘻,吴姐厉害大大滴。

这那一段仅仅针对去 ...

按照你的代码测试下来,是adox那段比较快,可是openschema慢在Set rst1 = Nothing和cnn.Close这两句上。这两句去掉速度就比adox快了。我觉得rst1反正要重新set的,所以我测试的时候没有set nothing,cnn反正是要set nothing,所以没有close。(我这个做法可能不正规。)
另外,用了adox,还是要判断表类型是否是TABLE,而openschema的第二参数是可以把TABLE类型直接过滤出来的。

TA的精华主题

TA的得分主题

发表于 2012-1-31 23:36 | 显示全部楼层
zhaogang1960 发表于 2012-1-31 22:44
我这里测试也是这样,还没有细看
有收获,本来要求速度目的是为了避免抄袭论坛上已经有的,两次打开文件 ...

赵老师,关于这2个方法,速度上差别在于openschema需要把cnn关闭掉,才能重新打开一个新的连接。每关闭一次连接时间大概在0.3秒,运行20次,差不多在6秒多。如果使用多次连接法的话,速度反而会快。运用上来讲还是用adox比较好。

点评

经测试是这样  发表于 2012-2-1 00:18
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

关闭

最新热点上一条 /1 下一条

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-4-25 15:41 , Processed in 0.040903 second(s), 15 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表