如何提取Summary表未包含的客户数据并填充至ListBox?
需求说明
我拥有两个工作表:Summary表和Data表。Summary表中包含特定客户编号的汇总数据,需要提取Data表中所有未在Summary表中出现的客户数据,按照Summary表的列结构(客户编号、客户名称、账龄、发票数量、金额)进行汇总,并将结果填充至ListBox组件中。
Summary表数据
| 客户编号 | 客户名称 | 账龄 | 发票数量 | 金额 |
|---|---|---|---|---|
| 55850 | ABC | 1-30 | 2 | 2516 |
| 55850 | ABC | 30-60 | 1 | 52635 |
| 55850 | ABC | 60-90 | 1 | 102754 |
| 55850 | ABC | 90-120 | 1 | 152873 |
| 55850 | ABC | 120-180 | 1 | 202992 |
| 32336 | DEF | 30-60 | 2 | 253111 |
| 32336 | DEF | 60-90 | 2 | 303230 |
| 30131 | GHI | 1-30 | 1 | 353349 |
| 30131 | GHI | 30-60 | 2 | 403468 |
| 30131 | GHI | 60-120 | 2 | 453587 |
| 13914 | JKL | 1-30 | 2 | 503706 |
| 13914 | JKL | 30-60 | 2 | 553825 |
| 13914 | JKL | 60-90 | 2 | 603944 |
| 13914 | JKL | 90-120 | 1 | 654063 |
Data表数据
| 客户编号 | 客户名称 | 发票编号 | 发票日期 | Flag | 账龄 | 金额 | area |
|---|---|---|---|---|---|---|---|
| 55850 | ABC | 121 | 01/01/2022 | Yes | 1-30 | 1258 | ES |
| 55850 | ABC | 122 | 02/01/2022 | Yes | 1-30 | 1258 | WE |
| 55850 | ABC | 123 | 03/01/2022 | Yes | 30-60 | 52635 | NO |
| 55850 | ABC | 124 | 04/01/2022 | No | 60-90 | 102754 | SO |
| 55850 | ABC | 125 | 05/01/2022 | Yes | 90-120 | 152873 | ES |
| 55850 | ABC | 126 | 06/01/2022 | Yes | 120-180 | 202992 | WE |
| 32336 | DEF | 127 | 07/01/2022 | No | 30-60 | 126555.5 | NO |
| 32336 | DEF | 128 | 08/01/2022 | Yes | 30-60 | 126555.5 | SO |
| 32336 | DEF | 129 | 09/01/2022 | Yes | 60-90 | 151615 | ES |
| 32336 | DEF | 130 | 10/01/2022 | No | 60-90 | 151615 | WE |
| 30131 | GHI | 131 | 11/01/2022 | Yes | 1-30 | 353349 | NO |
| 30131 | GHI | 132 | 12/01/2022 | Yes | 30-60 | 201734 | SO |
| 30131 | GHI | 133 | 13/01/2022 | No | 30-60 | 201734 | ES |
| 30131 | GHI | 134 | 14/01/2022 | No | 60-120 | 226793.5 | WE |
| 30131 | GHI | 135 | 15/01/2022 | Yes | 60-120 | 226793.5 | NO |
| 13914 | JKL | 136 | 16/01/2022 | Yes | 1-30 | 251853 | SO |
| 13914 | JKL | 137 | 17/01/2022 | Yes | 1-30 | 251853 | ES |
| 13914 | JKL | 138 | 18/01/2022 | Yes | 30-60 | 276912.5 | WE |
| 13914 | JKL | 139 | 19/01/2022 | Yes | 30-60 | 276912.5 | NO |
| 13914 | JKL | 140 | 20/01/2022 | Yes | 60-90 | 301972 | SO |
| 13914 | JKL | 141 | 21/01/2022 | Yes | 60-90 | 301972 | ES |
| 13914 | JKL | 142 | 22/01/2022 | Yes | 90-120 | 654063 | WE |
| 13900 | LMN | 143 | 23/01/2022 | No | 1-30 | 120000 | NO |
| 13900 | LMN | 144 | 24/01/2022 | Yes | 30-60 | 180000 | SO |
| 13900 | LMN | 145 | 25/01/2022 | Yes | 60-90 | 50000 | ES |
| 13900 | LMN | 146 | 26/01/2022 | No | 90-120 | 60000 | WE |
| 13900 | LMN | 147 | 27/01/2022 | Yes | 120-180 | 70000 | NO |
| 13901 | OOO | 148 | 28/01/2022 | Yes | 30-60 | 3000 | SO |
| 13901 | OOO | 149 | 29/01/2022 | No | 60-90 | 40000 | ES |
| 13901 | OOO | 150 | 30/01/2022 | No | 90-120 | 50000 | WE |
| 13901 | OOO | 151 | 31/01/2022 | No | 120-180 | 60000 | NO |
| 13902 | OOX | 152 | 01/02/2022 | No | 30-60 | 10000 | SO |
| 13902 | OOX | 153 | 02/02/2022 | No | 60-90 | 20000 | ES |
| 13902 | OOX | 154 | 03/02/2022 | Yes | 90-120 | 30000 | WE |
| 13902 | OOX | 155 | 04/02/2022 | Yes | 120-180 | 40000 | NO |
原VBA代码问题分析
原代码存在以下关键错误:
- SQL字段名不匹配:使用英文字段名,但实际工作表列名为中文,无法正确读取数据。
- SQL语法错误:SELECT子句中字段间缺少逗号,导致SQL执行失败。
- 连接字符串语法错误:
.Open语句位置错误,需在设置完ConnectionString后单独调用。 - 字符串处理错误:Getcust函数中截取字符串的长度计算错误,无法正确生成SQL IN子句所需格式。
- ListBox数据赋值错误:
rs.GetRows返回的是转置数组,直接赋值会导致数据列行颠倒。 - ListBox清空方式错误:
.Value = ""无法有效清空列表,应使用.Clear。
修正后的VBA代码
Option Explicit Sub LoadUnlistedCustomersToLBox() Dim conn As Object, rs As Object, sqlStr As String, excludedCusts As String ' 创建并打开数据库连接 Set conn = CreateObject("ADODB.Connection") With conn .Provider = "Microsoft.ACE.OLEDB.12.0" .ConnectionString = "Data Source=" & ThisWorkbook.FullName & _ ";Extended Properties=""Excel 12.0 Xml;HDR=Yes;IMEX=1""" .Open End With ' 获取需要排除的客户编号字符串 excludedCusts = GetExcludedCustomers() ' 构建SQL查询语句:汇总未在Summary表中的客户数据 sqlStr = "SELECT T1.[客户编号], T1.[客户名称], T1.[账龄], " & _ "COUNT(T1.[发票编号]) AS [发票数量], SUM(T1.[金额]) AS [金额] " & _ "FROM [Data$] T1 " & _ "WHERE T1.[客户编号] NOT IN (" & excludedCusts & ") " & _ "GROUP BY T1.[客户编号], T1.[客户名称], T1.[账龄]" ' 执行查询并获取结果集 Set rs = conn.Execute(sqlStr) ' 将结果填充到ListBox If Not rs.EOF Then With Me.ListBox1 .Clear ' 转置GetRows返回的数组,匹配ListBox的列行结构 .Column = TransposeArray(rs.GetRows) .ColumnCount = rs.Fields.Count .ColumnWidths = "90,90,50,50,180" End With End If ' 清理资源 rs.Close conn.Close Set rs = Nothing Set conn = Nothing End Sub ' 获取Summary表中所有唯一客户编号,格式化为SQL IN子句需要的字符串 Private Function GetExcludedCustomers() As String Dim custDict As Object, custData As Variant, i As Long, result As String Set custDict = CreateObject("Scripting.Dictionary") ' 读取Summary表的客户编号列数据 custData = Sheets("Summary").ListObjects(1).ListColumns("客户编号").DataBodyRange.Value ' 去重存储客户编号 For i = LBound(custData, 1) To UBound(custData, 1) custDict(custData(i, 1)) = Empty Next i ' 拼接为带单引号的字符串,适配SQL IN语法 If custDict.Count > 0 Then result = "'" & Join(custDict.Keys(), "','") & "'" Else ' 若没有需要排除的客户,返回一个永远不匹配的条件 result = "'-1'" End If GetExcludedCustomers = result End Function ' 转置二维数组,适配ListBox的Column属性要求 Private Function TransposeArray(inputArr As Variant) As Variant Dim outputArr As Variant, i As Long, j As Long Dim rows As Long, cols As Long rows = UBound(inputArr, 2) + 1 cols = UBound(inputArr, 1) + 1 ReDim outputArr(0 To rows - 1, 0 To cols - 1) For i = 0 To rows - 1 For j = 0 To cols - 1 outputArr(i, j) = inputArr(j, i) Next j Next i TransposeArray = outputArr End Function
代码说明
- 连接修正:修正ConnectionString语法,确保正确将Excel文件作为数据源打开。
- SQL语句修正:使用中文列名,补充字段间逗号,保证语法正确且列结构与Summary表一致。
- 客户编号去重:用Dictionary高效获取Summary表中的唯一客户编号,拼接成符合SQL IN子句的格式,避免重复判断。
- 数组转置:新增
TransposeArray函数,将ADODB返回的转置数组转换为ListBox所需结构,确保数据显示正确。 - ListBox操作优化:使用
.Clear清空列表,设置正确的列宽和列数。
内容的提问来源于stack exchange,提问作者Mo007
相关产品推荐
相关产品推荐

