VBA在处理大量的数据/计算时如果使用常规方法会比较慢,因此需要对其进行性能优化以提高运行速度,一般的方法是数组计算或者sql计算。SQL计算的速度最快,限制也是最多的,数组速度其次,灵活性也更高
如果要在vba中调用sql处理数据基本可以遵循一个套路,只要修改其中的SQL语句即可
调用sql处理数据VBA代码如下,其中’##### #####中的地方是每次运行时要根据情况修改的:
Sub Sql_Query() Dim Conn As Object, Rst As Object Dim strConn As String, strSQL As String Dim i As Integer, PathStr As String Set Conn = CreateObject("ADODB.Connection") Set Rst = CreateObject("ADODB.Recordset") PathStr = ThisWorkbook.FullName Select Case Application.Version * 1 '设置连接字符串,根据版本创建连接(不同版本的excel连接是不同的) Case Is <= 11 strConn = "Provider=Microsoft.Jet.Oledb.4.0;Extended Properties=excel 8.0;Data source=" & PathStr Case Is >= 12 strConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & PathStr & ";Extended Properties=""Excel 12.0;HDR=YES"";""" End Select strSQL = "Select * FROM [rawdata$]" '####在这里改SQL查询语句#### Conn.Open strConn '打开数据库链接 Set Rst = Conn.Execute(strSQL) '执行查询,并将结果输出到记录集对象 With ThisWorkbook.Sheets("sql data") '#####在这里更改输出的位置对应的表名#### .Cells.Clear For i = 0 To Rst.Fields.Count - 1 '填写标题 .Cells(1, i + 1) = Rst.Fields(i).Name '在第一行输出字段名 Next i .Range("A2").CopyFromRecordset Rst '从A2单元格开始输出 .Cells.EntireColumn.AutoFit '自动调整列宽 End With Rst.Close '关闭数据库连接 Conn.Close Set Conn = Nothing Set Rst = Nothing End Sub
接下来,开始学习SQL语句语法
SQL语法格式比较固定,只需要根据特定的语法顺序,再根据需求加减关键字即可。首先我们给出一个比较全的格式如下:
SELECT [DISTINCT] [TOP <数值> [PERCENT]] 列标题 [聚合函数] [[AS] <别名列标题>] FROM 表或查询 [[AS] <别名>] [WHERE <筛选条件>] [ORDER BY <排序项> [ASC|DESC]]
说明:
接下来详细讲解每一个关键字的使用:
SELECT关键字和FROM 关键字是每个SQL语句中都必须要有的关键字。SELECT关键字可以选取最终需要显示所有字段,而FROM关键字则用于告诉程序数据源的位置在哪里。VBA中一个最基本的SQL语句写法如下:
SELECT 列字段名1,列字段名2,列字段名3...... FROM [工作表名称$]
例如我们需要从student这一个sheet中取出ID,name,address,score这四列数据:
select ID,name,address,score from [student$]
也可以使用[]将列名括起来表示这是一个列名
select [ID],[name],[address],[score] from [student$]
如果需要取所有的列,可以直接使用“*”号来取全部的列,从而减少代码量
#*号是代表全部列 select * from [sheet1$]
AS关键字用于给字段或者数据源表取别名,从而简化一些比较长的表名或字段名
#从student表中选取姓名列,并重命名为Name SELECT 姓名 AS Name FROM [Student$]
DISTINCT关键字功能是去重,语法结构为:
SELECT DISTINCT 要去重复值的字段1,要去重复值的字段2...... FROM [工作表名$]
举例如下:
#选取所有不重复的name Select Distinct name From [student$] #选取name和ID都不重复的数据 Select Distinct name,ID From [student$]
where关键字可以按条件筛选数据。WHERE语句后可以有一个或多个条件,条件之间可以用操作符AND 或者OR进行连接。
WHERE语句的语法结构如下:
SELECT 列字段名称 FROM [表名称$] WHERE 条件
where后的条件语句可以是运算符或逻辑判断,包含大于、小于、等于、不等于、大于或等于、小于或等于、IN、 BETWEEN、AND、NOT等。
#选取取消费金额等于100的数据 SELECT * FROM [test$] where 消费金额=100 #选取级别为A的数据 SELECT * FROM [test$] where 级别='A' #选取级别不为A的数据 SELECT * FROM [test$] where 级别<>'A' #选取级别为A,B,C,D的数据 SELECT * FROM [test$] where 级别 in ('A','B','C','D') #选取取消费金额不为空的数据 SELECT * FROM [test$] where 消费金额 is not NULL #选取取消费金额大于100且级别为A的数据 SELECT * FROM [test$] where 消费金额>100 AND 级别='A'
ORDER BY关键字可以对结果排序,升序的关键词为ASC,也是默认值,通常不指定。降序为DESC。语法结构如下:
SELECT 列字段名 FROM [工作表名称$] ORDER BY 指定列字段名 升序(降序)
按成绩进行升序排序的SQL语句如下:
Select * FROM [Sheet1$] ORDER BY 成绩 ASC
,如果要按两个或两个以上字段,那么字段与字段之间用号逗隔开,
TOP按顺序提取前n行的记录,语法结构如下:
SELECT TOP 3 * FROM [工作表名$]
如提取成绩前三名的记录
Select top 3 * FROM [Sheet1$A1:C17] ORDER BY score
其中[Sheet1$A1:C17]这个表示工作表名Sheet1的工作表A1:C17的这个单元格区域,加上了指定的单元格区域为数据。可以根据自己的实际情况,来改变.
不在同一张表上显示结果,而且数据源规范,就可以直接用[工作表名$]。或者书写的时候写上列名不用星号(“*”)
这两个也是和标准sql之间差别较大的地方
TOP 与 PERCENT 组合在SQL语句中的使用可以按照百分比提取数据,例如按成绩降序排列提取前30%
Select TOP 30 PERCENT * FROM [Sheet1$A1:C17] ORDER BY 成绩 DESC
SQL聚合函数包括sum,count,avg,max,min等,与excel的公式有同样的效果,但是在大数据量的情况下速度会提升很多,而且在多条件求和时会比使用sumifs简单,直观的多
多条件求和的语法结构为:
SELECT 分类字段1, 分类字段2 , SUM(统计字段) as 产品总数 From [Sheet1$] GROUP BY 分类字段1, 分类字段2
例如按照日期和姓名汇总产品数:
SELECT 日期, 姓名, SUM(产品数) as 产品总数 From [Sheet1$] GROUP BY 日期, 姓名
注意事项:
1.在SQL语句中SQL语句英文不区分大小写,但标点符号必须是英文半角状态下输入,字段名也必须跟原来的一样。
2.使用SQL语句的时候,必须避免列字段中使用下面的特殊字符:
空格、双引号(")、撇(')、数字标记(#)、百分号(%)、大于号(>)、小于号(<)、叹号(!)、句号(.)、方括号([或])、星号(*)、美元符号($)、分号(;)、脱字号(^)、圆括号((或))、加号(+)、反斜杠(\或/)。
如果在源数据表的列字段使用了这些特殊字符,那么在使用SQL语句列出各字段的数据时,就会发生错误。为了规范使用SQL语句,在对数据源字段命名时,尽量避免使用这些特殊字符。
在SQL表达式运算符条件中,要查询日期和时间类型的数据,需要在数据值两端加上井字符号(#)以表示日期类型。
日期可以有多种表示方式,最符合中国人的习惯是“年-月-日”或“年/月/日”的表示方式。也就是说年月日之间的分隔符可以用“-”或“/”。
例如表示2013年10月1日
可以采用以下表达式:
年月日 #2013-10-1# 年日月 #2013-1-10# 年月日 #2013/10/1# 日月年 #1/10/2013/# 月日年 #10/1/2013# 以表达式在系统无错识别最高的应该是 月/日/年 #10/1/2013#
Where … AND…语句,例如查找大于等于2013年10月1日,小于等于2013年10月7日的数据,的SQL表达式为:
SELECT * From [Sheet1$A:C] where 日期>=#10/1/2013# And 日期<=#10/7/2013#
还可以以单元为动态引用查询两个日期内的数据,如下图所示
strSQL = "SELECT * From [Sheet1$A:C] where 日期>=#" & Range("J1") & "# And 日期<=#" & Range("K1") & "#"
Like 操作符用于在 WHERE 子句中搜索列中的模糊匹配
Select 列字段名 From [工作表名$] Where 列字段 Like '关键字'
可以加上通配符% (相当于函数公式中的通配符*)
strSQL = "Select * from [Sheet1$] Where 姓名 like '[AB]%'"
这一语句表示匹配姓名以A开头或B开头的数据
同样的可以反向查询姓名不以A开头或B开头的数据
strSQL = "Select * from [Sheet1$] Where 姓名 like '[!AB]%'" --or strSQL = "Select * from [Sheet1$] Where not 姓名 like '[AB]%'"
In运算符允许 在 WHERE 子句中规定多个值,语法如下:
Select 字段 From [表名$] Where 字段 In(条件1, 条件2,条件3, ....)
如果在In的条件中不是数值类型,一定要加上引号。
eg:
Select * FROM [Sheet1$A:D] where 省份 in('广东','广西')
将两表连接起来的语句一般是UNION和UNION ALL
UNION ALL是查询所有记录(直接连接,可以重复),UNION只查询不重复(指整条记录不重复,取唯一)的记录,两种语句如果有重复记录,则查询结果就不一样。
在数据库中,UNION和UNION ALL关键字都是将两个结果集合并为一个,但这两者从使用和效率上来说都有所不同。
UNION在运行时先取出几个表的结果,再用排序空间进行排序删除重复的记录,最后返回结果集,如果表数据量大的话可能会导致用磁盘进行排序。
而UNION ALL只是简单的将两个结果合并后就返回。这样,如果返回的两个结果集中有重复的数据,那么返回的结从效率上说,UNION ALL 要比UNION快很多,所以,如果可以确认合并的两个结果集中不包含重复的数据的话,那么就使用UNION ALL。
例如将sheet1与sheet2中的数据连接起来:
SELECT 日期,姓名,产品数 From [Sheet1$] UNION SELECT 日期,姓名,产品数 From [Sheet2$]
这个是在两张表的格式一样的情况下直接连接,但是有些情况下两表的格式并不一样,这个时候可以使用下列语句:
例如sheet1中有日期,姓名两个字段,而sheet2中有姓名,产品数两个字段,需要上下连接两个表格:
SELECT 日期,姓名,null as 产品数 From [Sheet1$] UNION SELECT null as 日期,姓名,产品数 From [Sheet2$]
即使用null加列拼成格式相同的两个表格再连接
两表的左右连接即是join关键字
SQL代替Vlookup 精确查找(左连接 “Left Outer JOIN” 用法),语法结构为:
Select 表名 .字段1,表名 .字段2,表名 .字段3,表名 .字段4 FROM 查询表 AS 别名1 Left Outer JOIN 被查询表 AS 别名2 ON 别名1.字段名=别名2.字段名
例如:
SELECT A.姓名,B.性别,B.部门 FROM [Sheet2$] AS A Left Outer JOIN [Sheet1$] AS B ON A.姓名=B.姓名 --简写为 SELECT A.姓名,性别,部门 FROM [Sheet2$]A Left JOIN [Sheet1$]B ON A.姓名=B.姓名
join除了left join外还有right join,inner join等,之间的区别与sql语句都是一致的。注意VBA中的SQL是不支持outer join连接的
如果需要连接多个表,可以按照上述两表连接的语法写多次即可。注意语句中的括号,在VBA SQL中不使用括号会导致错误
select A.姓名,B.性别,C.部门 from ([Sheet2$] as A left JOIN [Sheet1$] as B on A.姓名=B.姓名) left JOIN [Sheet3$] as C on A.姓名=C.姓名
TRANSFORM语句可以实现像透视表一样的显示结果,语法结构为:
TRANSFORM 汇总方式 SELECT 行标签 from 表名 group by 行字段 PIVOT 列标签
这一语句是非常常用的方法,要注意TRANSFORM后的汇总方式为一个聚合函数(一般为sum),同时后续的SELECT语句中不要加入a字段/b字段或者having语句,否则会导致语句错误无法运行
eg:
原始数据表如下
运行如下语句输出数据透视表:
TRANSFORM Sum(数量) Select 产品名称 from [Sheet1$] group by 产品名称 PIVOT 款号
可得到如下结果:
TRANSFORM语句话还可以加入一些自定义的设置:
TRANSFORM 汇总方式 SELECT 行标签 from 表名 group by 行字段 PIVOT 列标签 in [列标签值1,列标签值2,列标签值3.....]
使用in可以限制列标签的内容,同时可以规定列字段值的展示顺序
在SQL中使用广泛的case关键字在VBA SQL中的实现方式为IIF,IIF的语法为:
IIf(Logical EXPression, Numeric Expression1, Numeric Expression2)
如果 Logical Expression 取值为 TRUE,则此函数返回 Numeric Expression1,否则,返回 Numeric Expression2。
例如:
当site列填NA时,将if_address设为N,否则设为Y
select name,iif([site]='NA','N','Y') as if_address form [student$]
同理,Logical Expression可以使用isnull,数字等值判断语句等
同时iif语句对符合条件的数据进行计算。例如如果是赠送的,实际的花费为0,否则为原价购买计算花费
select 客户,sum(iif(buying_type='赠送',price,0)) as cost from [order$]
如果SQL中的数据涉及到不同的Excel表,可以直接从其他文件获取数据的SQL语句:
Select * From [盘符:\路径\工作簿名1.后辍名].[工作表名$] Union All Select * From [盘符:\路径\工作簿名2.后辍名].[工作表名$] Union All Select * From [盘符:\路径\工作簿名3.后辍名].[工作表名$]
如果工作表没有列标题,用F1,F2,F3,F4…这样代替
如果列标题中有特殊字符如单引号、斜杠、百分号等。或者关键字:from、in、select、where等,可以用[ ]将字段包起来表示这是一个字段名,例如:
Select [%百分比] FROM [Sheet1$]