ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

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

[原创] 用字典+ADO查询实现计费表格查询

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2014-3-7 00:04 | 显示全部楼层 |阅读模式
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
本帖最后由 zhaogang1960 于 2014-3-7 15:46 编辑

用字典+ADO查询实现计费表格查询

今天遇到一个根据邮寄地点、邮件分量查表计费的题目,觉得挺有参考价值,现专门发帖分享。原帖网址:
运费计算

要求:
A列为每个不重复订单的重量合计值,每个订单的运费计算需判断的因素有:库房编码、快递公司、目的省、目的市、重量,而运算因素有:首重、首重收费、续重1KG收费、上门服务费,订单运费=首重收费+续重1KG收费*(重量-首重)+上门服务费。如何得出每个订单的运费。见下图。

1.JPG 2.JPG


本题目有5个条件:
目的省        目的市       快递公司        库房编码  订单重量

前4个用来定位行号,是一对多的关系,即相同的条件对应着订单表的多行,有两种情况
不考虑目的市,考虑目的市——通过对应的运费标准表中是否是省内所有来决定用哪一个,且考虑目的市优先
SQL语句用左外连接实现先不考虑订单重量,而把所有可能的匹配罗列出来

在众多符合前4个条件的数据中,再用订单重量来判断是否符合条件——If Application.Evaluate(arr(0, i) & arr(8, i)) Then
优先考虑目的市,即有目的市时直接写数据,没有目的市时要先判断改行数据是否已经有数据了,如果有就不再覆盖了。代码如下:

  1. Sub 计算订单运费()
  2.     Dim cnn, SQL$, a, arr, brr(), i&, j&, s$, d As Object, ds As Object
  3.     Set d = CreateObject("scripting.dictionary") '创建字典对象,以储存库房编码、快递公司、目的省字符串确定的行号
  4.     Set ds = CreateObject("scripting.dictionary") '创建字典对象,以储存库房编码、快递公司、目的省、目的市字符串确定的行号
  5.     arr = [a1].CurrentRegion '“订单”工作表数据写入数组
  6.     ReDim brr(2 To UBound(arr), 1 To 1) '重新定义数组,行数等于数据行数
  7.     For i = 2 To UBound(arr)
  8.         s = arr(i, 8) & arr(i, 7) & arr(i, 5) & arr(i, 1) '库房编码、快递公司、目的省字符串
  9.         d(s) = d(s) & "," & i '库房编码、快递公司、目的省添加到字典键值,行号添加到字典条目
  10.         s = arr(i, 8) & arr(i, 7) & arr(i, 5) & arr(i, 1) & arr(i, 6) '库房编码、快递公司、目的省、目的市字符串
  11.         ds(s) = ds(s) & "," & i '库房编码、快递公司、目的省、目的市添加到字典键值,行号添加到字典条目
  12.     Next
  13.     Set cnn = CreateObject("adodb.connection") '创建ADO连接对象
  14.     cnn.Open "Provider=Microsoft.ace.OLEDB.12.0;Extended Properties=Excel 12.0;Data Source=" & ActiveWorkbook.FullName '连接数据库
  15.     'SQL语句用左外连接实现先不考虑订单重量,而把所有可能的匹配罗列出来
  16.     SQL = "select a.订单重量,a.订单运费,a.订单编码,a.重量,a.目的省,a.目的市,a.快递公司,a.库房编码,b.重量,b.首重,b.首重收费,b.续重1KG,b.上门服务费,b.目的市 from [订单[        DISCUZ_CODE_0        ]quot; _
  17.         & [a1].CurrentRegion.Address(0, 0) & "] a left join [运费标准$] b on a.库房编码=b.库房编码 and a.快递公司=b.快递公司  and  a.目的省=b.目的省"
  18.     arr = cnn.Execute(SQL).GetRows 'SQL查询结果存放到数组arr
  19.     For i = 0 To UBound(arr, 2) '逐行数据
  20.         If Not IsNull(arr(0, i)) Then '排除“订单重量”为空值的数据行
  21.             If Application.Evaluate(arr(0, i) & arr(8, i)) Then '“订单重量”符合“运费标准”表重量要求,如<=3、>10等
  22.                 If arr(13, i) <> "省内所有" Then '有“目的市”
  23.                     a = Split(ds(arr(7, i) & arr(6, i) & arr(4, i) & arr(0, i) & arr(13, i)), ",") '由库房编码、快递公司、目的省、目的市字符串确定的字典条目所有行号数组
  24.                     For j = 1 To UBound(a) '逐个行号
  25.                         brr(Val(a(j)), 1) = arr(10, i) + (arr(0, i) - arr(9, i)) * arr(11, i) + arr(12, i) '写订单运费
  26.                     Next
  27.                 Else '没有“目的市”
  28.                     a = Split(d(arr(7, i) & arr(6, i) & arr(4, i) & arr(0, i)), ",") '由库房编码、快递公司、目的省字符串确定的字典条目所有行号数组
  29.                     For j = 1 To UBound(a) '逐个行号
  30.                         If brr(Val(a(j)), 1) = "" Then brr(Val(a(j)), 1) = arr(10, i) + (arr(0, i) - arr(9, i)) * arr(11, i) + arr(12, i) '写订单运费,与上面不同如果改行已经有值,则不再覆盖,以确保优先考虑有“目的市”
  31.                     Next
  32.                 End If
  33.             End If
  34.         End If
  35.     Next
  36.     [b2].Resize(UBound(brr) - 1) = brr '向“订单”工作表B列写数据
  37.     cnn.Close
  38.     Set cnn = Nothing
  39. End Sub
复制代码


附件:
字典+ADO外连接实现计费表格查询.rar (114.54 KB, 下载次数: 561)


本方本仅为了探讨字典结合ADO查询解决问题的方法,对于数据源已经打开的情况,还是用数组+字典速度更快,方法详见4楼。
  1. 该贴已经同步到 zhaogang1960的微博
复制代码


评分

8

查看全部评分

TA的精华主题

TA的得分主题

发表于 2014-3-7 07:05 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
谢谢赵版分享,好好学习。

TA的精华主题

TA的得分主题

发表于 2014-3-7 09:38 | 显示全部楼层

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-3-7 15:49 | 显示全部楼层
本帖最后由 zhaogang1960 于 2014-3-10 11:46 编辑

1楼方本仅为了探讨字典结合ADO查询解决问题的方法,对于数据源已经打开的情况,还是用数组+字典速度更快,代码如下:
  1. Sub 计算订单运费字典法()
  2. tt = Timer
  3.     Dim a, arr, ary, brr(), i&, j&, s$, d As Object, ds As Object
  4.     Set d = CreateObject("scripting.dictionary") '创建字典对象,以储存库房编码、快递公司、目的省字符串确定的行号
  5.     Set ds = CreateObject("scripting.dictionary") '创建字典对象,以储存库房编码、快递公司、目的省、目的市字符串确定的行号
  6.     arr = [a1].CurrentRegion '“订单”工作表数据写入数组
  7.     ReDim brr(2 To UBound(arr), 1 To 1) '重新定义数组,行数等于数据行数
  8.     For i = 2 To UBound(arr)
  9.         If Len(arr(i, 1)) Then '排除“订单重量”为空值的数据行
  10.             s = arr(i, 8) & arr(i, 7) & arr(i, 5)  '库房编码、快递公司、目的省字符串
  11.             d(s) = d(s) & "," & i '库房编码、快递公司、目的省添加到字典键值,行号添加到字典条目
  12.             s = s & arr(i, 6) '库房编码、快递公司、目的省、目的市字符串
  13.             ds(s) = ds(s) & "," & i '库房编码、快递公司、目的省、目的市添加到字典键值,行号添加到字典条目
  14.         End If
  15.     Next
  16.     ary = Sheets("运费标准").[a1].CurrentRegion  '“运费标准”工作表数据写入数组ary
  17.     With Application
  18.         For i = 2 To UBound(ary) '逐行数据
  19.             s = ary(i, 1) & ary(i, 2) & ary(i, 3) & ary(i, 4) '库房编码、快递公司、目的省、目的市字符串
  20.             If ds.Exists(s) Then '如果"运费标准"表中有"目的市"
  21.                 a = Split(ds(s), ",") '由库房编码、快递公司、目的省、目的市字符串确定的字典条目所有行号数组
  22.                 For j = 1 To UBound(a) '每个字典ds条目储存的行号
  23.                     r = Val(a(j)) '行号
  24.                     If .Evaluate(arr(r, 1) & ary(i, 5)) Then '如果"订单重量"符合"运费标准"表中的重量标准
  25.                         brr(r, 1) = ary(i, 7) + (arr(r, 1) - ary(i, 6)) * ary(i, 8) + ary(i, 9) '写订单运费
  26.                     End If
  27.                 Next
  28.             Else '如果"运费标准"表中没有"目的市"
  29.                 s = ary(i, 1) & ary(i, 2) & ary(i, 3)  '库房编码、快递公司、目的省字符串
  30.                 If d.Exists(s) Then
  31.                     a = Split(d(s), ",")
  32.                     For j = 1 To UBound(a)
  33.                         r = Val(a(j))
  34.                         If .Evaluate(arr(r, 1) & ary(i, 5)) Then
  35.                             If Len(brr(r, 1)) = 0 Then brr(r, 1) = ary(i, 7) + (arr(r, 1) - ary(i, 6)) * ary(i, 8) + ary(i, 9) '写订单运费,与上面不同如果改行已经有值,则不再覆盖,以确保优先考虑有“目的市”
  36.                         End If
  37.                     Next
  38.                 End If
  39.             End If
  40.         Next
  41.     End With
  42.     [b2].Resize(UBound(brr) - 1) = brr '向“订单”工作表B列写数据
  43.     MsgBox Timer - tt
  44. End Sub
复制代码

两种方法比较.rar (124.64 KB, 下载次数: 306)

TA的精华主题

TA的得分主题

发表于 2014-3-7 16:42 | 显示全部楼层
zhaogang1960 发表于 2014-3-7 15:49
1楼方本仅为了探讨字典结合ADO查询解决问题的方法,对于数据源已经打开的情况,还是用数组+字典速度更快,代 ...

2种方法的结果不同。

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-3-7 16:56 | 显示全部楼层
本帖最后由 zhaogang1960 于 2014-3-10 11:48 编辑
42921783 发表于 2014-3-7 16:42
2种方法的结果不同。

把一次的结果复制到一个空列,如J列,再运行另一种方法,用下面公式可以测试出使用有错

=IF(B2<>J2,1,"")
在第一个程序结束时加一句:
Columns("B:B").Copy Range("J1") '    新加,用来测试两种方法结果是否一致
Sub 主程序()
    Call 计算订单运费
    Call 计算订单运费字典法
End Sub

运行主程序后,请查看K列是否有  1

两种方法比较.rar (225.7 KB, 下载次数: 132)

TA的精华主题

TA的得分主题

发表于 2014-3-7 17:07 | 显示全部楼层
zhaogang1960 发表于 2014-3-7 16:56
把一次的结果复制到一个空列,如J列,再运行另一种方法,用下面公式可以测试出使用有错

=IF(B2J2,1,"" ...

K列是有1。

点评

我测试没有1,请录制一个动画我看看  发表于 2014-3-7 17:09

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-3-7 17:21 | 显示全部楼层
本帖最后由 zhaogang1960 于 2014-3-10 11:49 编辑
42921783 发表于 2014-3-7 17:07
K列是有1。

请看运行演示及结果:
99.gif


运行结果.rar (230.01 KB, 下载次数: 64)

TA的精华主题

TA的得分主题

发表于 2014-3-7 17:37 | 显示全部楼层
zhaogang1960 发表于 2014-3-7 17:21
请看运行演示及结果:

如图,确实是有1出现。
图片.gif
图片.gif

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-3-7 17:40 | 显示全部楼层
42921783 发表于 2014-3-7 17:37
如图,确实是有1出现。

你这是什么版本的Excel?
请上传你运行程序后的工作簿我看看
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

关闭

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

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

GMT+8, 2024-4-20 07:02 , Processed in 0.054556 second(s), 13 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

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

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

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