ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

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

[求助] Excel + VBA + SQL 查询数据库,如何提速

[复制链接]

TA的精华主题

TA的得分主题

发表于 2021-10-25 00:10 | 显示全部楼层 |阅读模式
现在有通过信息搜集平台得到的Excel数据,需要用Excel中的数据,在数据库中查询对应信息。功能已经实现,但是速度很慢,1条1秒,1400条数据需要20多分钟,请教是否有合适的方法可以并行查询,或者提速

TA的精华主题

TA的得分主题

发表于 2021-10-25 08:20 | 显示全部楼层
1条1秒夸张了,说明你用的方法不对

TA的精华主题

TA的得分主题

发表于 2021-10-25 10:09 | 显示全部楼层
可以把要查询的数据,先批量写到sql数据库,然后再sql里查,将结果再批量返回excel,这样就能秒开了

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2021-10-25 10:38 | 显示全部楼层
不要一条一条处理,那样的话,不如数组字典来得快

TA的精华主题

TA的得分主题

 楼主| 发表于 2021-11-7 00:37 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖最后由 biaobro@sina 于 2021-11-7 09:05 编辑
笑着...两年过去 发表于 2021-10-25 10:09
可以把要查询的数据,先批量写到sql数据库,然后再sql里查,将结果再批量返回excel,这样就能秒开了

大侠  您好  您说的这个思路的具体做法 可否再具体指点一下   
我的数据如附件,A列单元格中保存的是订单号,有1个单元格保存1个订单的,还有1个单元格保存几个订单的,需要用这些订单号到数据库中查询对应订单数量(有效性),下单渠道(group_concat),订单金额(sum).

我现在做法是用For循环,读单元格,然后查询,代码如下。这样的话,1个单元格的查询加上回写就是1秒左右。 1400多行要20分钟。。。。

  1. Sub link_Order()
  2.     Dim conn As ADODB.Connection
  3.     Dim rs As ADODB.Recordset
  4.     Set conn = New ADODB.Connection
  5.     Set rs = New ADODB.Recordset
  6.     Dim strSql As String
  7.    
  8.     '配置连接串
  9.     conn.ConnectionString = "Driver={MySQL ODBC 8.0 Unicode Driver};Server=localhost;DB=ticket;UID=root;PWD=2020;OPTION=3;"
  10.     'conn.Open
  11.    
  12.     '从数据库表中取数据
  13.    
  14.    
  15.     For i = 2 To Range("A1").CurrentRegion.Rows.Count Step 1
  16.         conn.Open
  17.         
  18.         'sql 多行写的时候要特别注意分割以及空格
  19.         strSql = "SELECT count(*)," & _
  20.                 "GROUP_CONCAT( DISTINCT t.order_channel )," & _
  21.                 "SUM( t.tmoneysum )," & _
  22.                 "SUM( t.texpressfee ) " & _
  23.             "FROM" & _
  24.                 "(SELECT order_id," & _
  25.                         "order_channel," & _
  26.                         "sum( ticket_count ) AS tticektsum," & _
  27.                         "sum( ticket_price )," & _
  28.                         "sum( money_should_receive ) AS tmoneysum," & _
  29.                         "sum(express_fee) As texpressfee " & _
  30.                 "FROM " & _
  31.                         "order_detail_f " & _
  32.                 "WHERE " & _
  33.                         "order_id IN  (" & Cells(i, 1).Value & ") Group BY order_id ) t"

  34.         
  35.         Debug.Print (i)

  36.         rs.Open strSql, conn
  37.         
  38.         '将数据输出到工作表
  39.         Cells(i, 2).CopyFromRecordset rs
  40.         
  41.         '关闭连接
  42.         rs.Close
  43.         conn.Close
  44.     Next
  45.    

  46.     Set rs = Nothing
  47.     Set conn = Nothing
  48. End Sub

复制代码


order.zip

5.89 KB, 下载次数: 17

TA的精华主题

TA的得分主题

发表于 2021-11-7 15:06 | 显示全部楼层
"order_id IN  (" & Cells(i, 1).Value & ") Group BY order_id ) t"
先把所有单号放到一起再查询
For i = 2 To Range("A1").CurrentRegion.Rows.Count Step 1
     sql = sql & WorksheetFunction.Substitute(Cells(i, 1).Value, ",", "','") & "','"
Next
sql = "'" & Left(sql, Len(sql) - 2)
然后再执行查询
"order_id IN  (" & sql & ") Group BY order_id ) t"

TA的精华主题

TA的得分主题

 楼主| 发表于 2021-11-7 16:26 | 显示全部楼层
本帖最后由 biaobro@sina 于 2021-11-7 16:41 编辑
T100 发表于 2021-11-7 15:06
"order_id IN  (" & Cells(i, 1).Value & ") Group BY order_id ) t"
先把所有单号放到一起再查询
For i ...

谢谢指点。 把所有单号放在一起查的话,像A7 单元格这种,得到的是3条记录。还得再做合并处理:对数量和金额求和,对渠道进行去重拼接。 不知道有没有直接一步到位的办法。
而且在一些极端情况下,比如用户填写的3个单号,其中1个是错的,那数据库返回是2条。 如果直接按照3条记录拼接,就会导致A7后面的数据错乱所以,从单元格的角度看,还是整体查询,整体返回

1.png



TA的精华主题

TA的得分主题

 楼主| 发表于 2021-11-9 23:30 | 显示全部楼层
哈哈啊哈哈哈哈哈哈  搞出来了    7300行  20秒   虽然20s假死   但已经非常能接受了   

  1. Option Explicit

  2. Sub orderQuerybyCell()
  3.    
  4.     Dim t As Variant
  5.     t = Timer
  6.    
  7.     Const unitOrderLen = 18
  8.    
  9.     ' 字符串数组,用于存放单元格内容拆分后的结果
  10.     Dim cellStrArray() As String
  11.    
  12.     ' 外层循环计数器
  13.     Dim outerCounter As Integer
  14.     outerCounter = 0
  15.       
  16.     ' 内层循环计数器
  17.     Dim innerCounter As Integer
  18.     innerCounter = 0
  19.    
  20.     ' 订单字符串
  21.     Dim orderStr As String
  22.     orderStr = ""
  23.    
  24.     Dim finalOrderStr As String
  25.     finalOrderStr = ""
  26.    
  27.     Dim cell As Range
  28.     Dim element As Variant
  29.    

  30.     ' 清除格式
  31.     Selection.ClearFormats
  32.     ' 设置格式,分列成文本,避免科学计数法导致的问题
  33.     Selection.TextToColumns Destination:=Selection, DataType:=xlDelimited, FieldInfo:=Array(1, 2)
  34.    
  35.     ' 设置格式,单元格内容显示不超出单元格范围 不灵
  36.     'Selection.Resize(1, 8).EntireColumn.HorizontalAlignment = xlFill
  37.    
  38.     ' 1个备注行 存放问题订单
  39.     ' 1个备注行 存放正常订单号
  40.     ' 5个标题行 存放字段对应的数据
  41.     Selection.Offset(0, 1).Resize(1, 7).EntireColumn.Insert

  42.     ' 如果选中的是整列,那跳过第1行 标题行
  43.     If Selection.Address = Selection.EntireColumn.Address Then
  44.         'MsgBox "Yes,you select whole column"
  45.         Selection.Resize(ActiveCell.CurrentRegion.Rows.Count - 1, 1).Offset(1, 0).Select
  46.         Debug.Print Selection.Address
  47.     End If
  48.    
  49.     ' 第1大步,拼接查询条件。遍历所选区域单元格,拼接全部订单号,得到用于SQL查询的订单字符串
  50.     For Each cell In Selection
  51.         ' 长度大于18,即不止1个单号
  52.         If Len(cell) > unitOrderLen Then
  53.             cellStrArray = Split(cell.Value, ",")
  54.             For Each element In cellStrArray
  55.                 orderStr = orderStr + cellStrArray(innerCounter) + ","
  56.                 innerCounter = innerCounter + 1
  57.             Next
  58.             innerCounter = 0
  59.             orderStr = Left(orderStr, Len(orderStr) - 1)
  60.         '只有1个单号
  61.         Else
  62.             orderStr = orderStr + cell.Value
  63.         End If
  64.    
  65.         ' 将单元格内的字符串加到整体字符串上
  66.         finalOrderStr = finalOrderStr & orderStr & ","
  67.         orderStr = ""
  68.     Next
  69.    
  70.     ' 去掉末尾的逗号
  71.     finalOrderStr = Left(finalOrderStr, Len(finalOrderStr) - 1)
  72.     'Debug.Print finalOrderStr
  73.     Debug.Print Timer - t
  74.    
  75.    
  76.     ' 第2大步,查询数据
  77.     Dim conn As ADODB.Connection
  78.     Set conn = New ADODB.Connection
  79.    
  80.     Dim rs As ADODB.Recordset
  81.     Set rs = New ADODB.Recordset
  82.    
  83.     rs.CursorLocation = adUseClient
  84.    
  85.     '配置数据库连接串
  86.     conn.ConnectionString = "Driver={MySQL ODBC 8.0 Unicode Driver};Server=localhost;DB=ticket;UID=root;PWD=2020;OPTION=3;"
  87.     conn.Open
  88.   
  89.     '定义 SQL语句
  90.     Dim strSql As String
  91.     strSql = "SELECT order_id," & _
  92.                         "order_channel," & _
  93.                         "sum( ticket_count ) AS tticketsum," & _
  94.                         "sum( money_should_receive ) AS tmoneysum," & _
  95.                         "sum( express_fee ) As texpressfee " & _
  96.                 "FROM " & _
  97.                         "order_detail_f " & _
  98.                 "WHERE " & _
  99.                         "order_id IN  (" & finalOrderStr & ") Group BY order_id "

  100.     'Debug.Print strSql
  101.   
  102.     '从数据库表中取数据
  103.     rs.Open strSql, conn, adOpenDynamic, adLockReadOnly
  104.    
  105.     '将数据输出到工作表
  106.     'Range("B2").CopyFromRecordset rs
  107.     Debug.Print Timer - t
  108.    
  109.     ' 第3大步,处理数据
  110.    
  111.    
  112.     ' 存放正常订单号
  113.     Dim groupNormalOrder As String
  114.     groupNormalOrder = ""
  115.    
  116.     '存放问题订单号(可能是订单单号错误,也可能是订单已关闭)
  117.     Dim groupAbnormalOrder As String
  118.     groupAbnormalOrder = ""
  119.    
  120.   
  121.     ' 订单下单渠道,需要去重
  122.     Dim groupOrderChannel As String
  123.     groupOrderChannel = ""
  124.    
  125.     ' 订单总数量
  126.     Dim groupOrderCount As Integer
  127.     groupOrderCount = 0
  128.   
  129.     ' 票总数量
  130.     Dim groupTicketCount As Integer
  131.     groupTicketCount = 0
  132.    
  133.     ' 订单总金额
  134.     Dim groupOrderMoney As Long
  135.     groupOrderMoney = 0
  136.    
  137.     ' 快递费总金额
  138.     Dim groupExpressMoney As Long
  139.     groupExpressMoney = 0
  140.    
  141.     outerCounter = 0
  142.     innerCounter = 0
  143.    
  144.    
  145.     ' 遍历区域中的单元格
  146.     For Each cell In Selection

  147.         rs.MoveFirst
  148.         
  149.         ' 单元格长度是否大于18
  150.         If Len(cell) > unitOrderLen Then
  151.             innerCounter = 0
  152.             ' 拆分单元格 成数组
  153.             cellStrArray = Split(cell.Value, ",")
  154.             
  155.             ' 遍历数组中的元素,即单元格内的每条订单号
  156.             For Each element In cellStrArray
  157.                 rs.Find "order_id = '" & cellStrArray(innerCounter) & "'"
  158.                
  159.                 If rs.EOF Then
  160.                     'MsgBox ("订单不存在")
  161.                     groupAbnormalOrder = groupAbnormalOrder + cellStrArray(innerCounter) + ","
  162.                 Else
  163.                     '对下单渠道信息进行拼接,已存在的什么也不做,不存在的拼接
  164.                     If InStr(groupOrderChannel, rs.Fields("order_channel")) > 0 Then
  165.                      
  166.                     Else
  167.                       groupOrderChannel = groupOrderChannel + rs.Fields("order_channel") + ","
  168.                     End If
  169.                     
  170.                     '只对正常订单进行信息汇总
  171.                     groupNormalOrder = groupNormalOrder + cellStrArray(innerCounter) + ","
  172.                     groupOrderCount = groupOrderCount + 1
  173.                     groupTicketCount = groupTicketCount + rs.Fields("tticketsum")
  174.                     groupOrderMoney = groupOrderMoney + rs.Fields("tmoneysum")
  175.                     groupExpressMoney = groupExpressMoney + rs.Fields("texpressfee")
  176.                     
  177.                     innerCounter = innerCounter + 1
  178.                 End If
  179.                 ' 移动搜索游标到开头,否则是从上一个搜索结果的位置开始Find,不一定能查到结果
  180.                 rs.MoveFirst
  181.             Next
  182.             
  183.             If groupAbnormalOrder <> "" Then
  184.                 cell.Offset(0, 1) = Left(groupAbnormalOrder, Len(groupAbnormalOrder) - 1) + " 无对应订单信息"
  185.             Else
  186.                 groupAbnormalOrder = " "
  187.             End If
  188.             
  189.             cell.Offset(0, 2) = Left(groupNormalOrder, Len(groupNormalOrder) - 1)
  190.             cell.Offset(0, 3) = groupOrderCount
  191.             cell.Offset(0, 4) = groupTicketCount
  192.             cell.Offset(0, 5) = Left(groupOrderChannel, Len(groupOrderChannel) - 1)
  193.             cell.Offset(0, 6) = groupOrderMoney
  194.             cell.Offset(0, 7) = groupExpressMoney
  195.             
  196.             
  197.             ' 每次单元格处理完成,要清空变量。便于下个单元格使用
  198.             groupNormalOrder = ""
  199.             groupAbnormalOrder = ""
  200.             groupOrderCount = 0
  201.             groupTicketCount = 0
  202.             groupOrderChannel = ""
  203.             groupOrderMoney = 0
  204.             groupExpressMoney = 0
  205.             
  206.         ' 只有1条订单的情况
  207.         Else
  208.             rs.Find "order_id = '" & cell.Value & "'"
  209.             
  210.             If rs.EOF Then
  211.                 'MsgBox ("订单不存在")
  212.                 '找不到匹配对象时,应该什么都不做
  213.                
  214.                 cell.Offset(0, 1) = cell.Value + " 无对应订单信息"
  215.                 cell.Offset(0, 2) = " "
  216.                 cell.Offset(0, 3) = " "
  217.                 cell.Offset(0, 4) = " "
  218.                 cell.Offset(0, 5) = " "
  219.                 cell.Offset(0, 6) = " "
  220.                 cell.Offset(0, 7) = " "
  221.             Else
  222.                 cell.Offset(0, 1) = " "
  223.                 cell.Offset(0, 2) = cell.Value
  224.                 cell.Offset(0, 3) = 1
  225.                 cell.Offset(0, 4) = rs.Fields("tticketsum")
  226.                 cell.Offset(0, 5) = rs.Fields("order_channel")
  227.                 cell.Offset(0, 6) = rs.Fields("tmoneysum")
  228.                 cell.Offset(0, 7) = rs.Fields("texpressfee")
  229.             End If
  230.         End If
  231.     Next
  232.       
  233.    
  234.    
  235.     '关闭数据库连接
  236.     rs.Close
  237.     conn.Close
  238.    
  239.     '清理变量
  240.     Set rs = Nothing
  241.     Set conn = Nothing
  242.    

  243.     MsgBox "用时 " & (Timer - t) & "秒", vbInformation
  244. End Sub
复制代码


TA的精华主题

TA的得分主题

发表于 2021-11-10 13:59 来自手机 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
biaobro@sina 发表于 2021-11-9 23:30
哈哈啊哈哈哈哈哈哈  搞出来了    7300行  20秒   虽然20s假死   但已经非常能接受了

1,用存储过程
2,最好不要嵌套子查询
3,group字段 需要 索引

TA的精华主题

TA的得分主题

发表于 2021-11-10 14:04 | 显示全部楼层
既然用sql了,就不要用for循环了。不见得sql语句有多复杂要嵌套在for循环中。。。。
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

关闭

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

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

GMT+8, 2024-4-25 13:36 , Processed in 0.046424 second(s), 16 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

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

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

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