上一节我们演示了如何制作周期销售汇总表,本节我们在上节的基础上再进行一些变化,制作按月份的同比环比分析表,具体格式如下:
取值分析:
同比是上一年度相同月份与本月之比,当前分析年度是2022年,那同比取值就是2021年;
环比是上月与本月之比,取值范围是2021年12月到2022年11月;
整个报表需要取值的范围就是2021年1月至2022年12月。
字段分析:由于涉及到年度, 我们需要用函数构造出一个年度的字段,并按客户,年度,月度分组合计。
SQL语句如下:
Select a.FCustID,YEAR(a.fdate) AS FYear,MONTH(a.fdate) AS FMonth,SUM(b.famount) AS FAmt
From ICSale a LEFT JOIN ICSaleEntry b on a.FInterID=b.FInterID
WHERE YEAR(a.fdate) IN (2021,2022)
Group By a.FCustID,YEAR(a.fdate),MONTH(a.fdate)
从上面的报表格式中我们可以看出,分析报表有12个月和1个合计,每个月份有5列,分别是当月,上年同期,同比增长率,上期,环比增长率。共有1(客户)+13*5=66列。
先看1月份的情况,其他月份可以仿照1月编写:
本期:
CASE WHEN x.FYear=2022 AND x.FMonth=1 THEN x.FAmt ELSE 0 END AS M11
上年同期:
CASE WHEN x.FYear=2021 AND x.FMonth=1 THEN x.FAmt ELSE 0 END AS M12
上期:
CASE WHEN x.FYear=2021 AND x.FMonth=12 THEN x.FAmt ELSE 0 END AS M13
这里CASE WHEN 写法与上节的不一样,当只有一个条件且为等于时,可以写成:
CASE x.FMonth WHEN 1 THEN x.FAmt ELSE 0 END AS M1
如果有多个条件,直接写在WHEN后面。
数据分列后,我们就可以构造出最后的数据列:
本期求和:SUM(M11)
上年同期求和:SUM(M12)
上年同期增长率:
CASE WHEN SUM(M12)=0 THEN 0 ELSE (SUM(M11)-SUM(M12))/SUM(M12) END
上期求和:SUM(M13)
上期增长率:
CASE WHEN SUM(M13)=0 THEN 0 ELSE (SUM(M11)-SUM(M13))/SUM(M13) END
由于字符串较长,为了不出现编写错误,我们用循环语句来构造字符串。
最后的结果如下:
罗马不是一天建成的,学习更是如此。如果要熟练掌握查询语句,必须大量的练习。先模仿,再举一反三,才能融会贯通。在练习过程中如果有不明白的地方可以在评论区留言,我们共同探讨。
附源码:
Option Explicit
Private Sub CommandButton1_Click()
Dim ado As Object
Dim rst As Object
Dim str As String
Dim sql As String
Dim s As String
Dim y As Integer, y1 As Integer, i As Integer
Dim dbIP As String
Dim dbsa As String
Dim dbpwd As String
Dim dbname As String
Dim rs As Integer
'清屏
Range("6:" & Rows.Count).Clear
'如果没有录入查询年度和月份,退出
If Val(Range("B1")) = 0 Then Exit Sub
'如果有自动筛选,先取消自动筛选
If ActiveSheet.AutoFilterMode Then Range("A4").AutoFilter
'设置数据库连接字符串
dbIP = "(local)" '安装数据库的电脑IP地址,(local)代表本机
dbsa = "sa" 'SQLServer数据库的登录用户名
dbpwd = "123456" 'SQLServer数据库的登录密码
dbname = "AIS20210318095953" '需要提取数据的金蝶数据库名
str = "Provider=SQLOLEDB.1;"
str = str & "Data Source=" & dbIP & ";"
str = str & "Persist Security Info=True;"
str = str & "User ID=" & dbsa & ";"
str = str & "Password=" & dbpwd & ";"
str = str & "Initial Catalog=" & dbname & ";"
'建立数据库连接
Set ado = CreateObject("ADODB.Connection")
ado.Open str
y = Range("B1")
y1 = y - 1
'构造提取数据的SQL语句开始***************************************************
'提取明细数据
sql = "Select a.FCustID,YEAR(a.fdate) AS FYear,MONTH(a.fdate) AS FMonth,SUM(b.famount) AS FAmt "
sql = sql & "From ICSale a LEFT JOIN ICSaleEntry b on a.FInterID=b.FInterID "
sql = sql & "WHERE YEAR(a.fdate) IN (" & y1 & "," & y & ") "
sql = sql & "Group By a.FCustID,YEAR(a.fdate),MONTH(a.fdate)"
'构造本期,上年同期,上期数据
s = "Select x.FCustID"
For i = 1 To 12
'本期
s = s & ",CASE WHEN x.FYear=" & y & " AND x.FMonth=" & i & " THEN x.FAmt ELSE 0 END AS M" & i & "1"
'上年同期
s = s & ",CASE WHEN x.FYear=" & y1 & " AND x.FMonth=" & i & " THEN x.FAmt ELSE 0 END AS M" & i & "2"
'上期
If i = 1 Then
s = s & ",CASE WHEN x.FYear=" & y1 & " AND x.FMonth=" & 12 & " THEN x.FAmt ELSE 0 END AS M" & i & "3"
Else
s = s & ",CASE WHEN x.FYear=" & y & " AND x.FMonth=" & i - 1 & " THEN x.FAmt ELSE 0 END AS M" & i & "3"
End If
Next
s = s & ",CASE WHEN x.FYear=2022 THEN x.FAmt ELSE 0 END AS Y1"
s = s & ",CASE WHEN x.FYear=2021 THEN x.FAmt ELSE 0 END AS Y2"
sql = s & " From (" & sql & ") x"
'分组求和
s = "Select c.FName"
For i = 1 To 12
s = s & ",SUM(M" & i & "1),SUM(M" & i & "2),CASE WHEN SUM(M" & i & "2)=0 THEN 0 "
s = s & "ELSE (SUM(M" & i & "1)-SUM(M" & i & "2))/SUM(M" & i & "2) END"
s = s & ",SUM(M" & i & "3),CASE WHEN SUM(M" & i & "3)=0 THEN 0 "
s = s & "ELSE (SUM(M" & i & "1)-SUM(M" & i & "3))/SUM(M" & i & "3) END"
Next
s = s & ",SUM(Y1),SUM(Y2),CASE WHEN SUM(Y2)=0 THEN 0 ELSE (SUM(Y1)-SUM(Y2))/SUM(Y2) END"
sql = s & " From (" & sql & ") y LEFT JOIN t_Organization c on y.FCustID=c.FItemID "
sql = sql & "Group By c.FName Order By SUM(y.Y1) DESC"
'构造提取数据的SQL语句结束***************************************************
Set rst = ado.Execute(sql)
If Not rst.EOF Then Range("A6").CopyFromRecordset rst
rst.Close
Set rst = Nothing
Set ado = Nothing
'*******************设置报表格式*******************
'取消工作表显示网格线
ActiveWindow.DisplayGridlines = False
'先设置报表标题
With Range("A3:BL5")
.Font.Name = "微软雅黑" '字体名称
.Font.Size = 10 '字体大小
.Font.Color = RGB(255, 255, 255) '字体颜色
.Interior.Color = RGB(72, 99, 156) '背景色
.HorizontalAlignment = xlCenter '水平居中
.VerticalAlignment = xlCenter '垂直居中
End With
'设置表体格式
With Range("A6:BL" & Range("A" & Rows.Count).End(xlUp).Row)
.Font.Name = "宋体" '字体名称
.Font.Name = "Calibri" '数字使用的字体名称
.Font.Size = 10 '字体大小
.VerticalAlignment = xlCenter '垂直居中
End With
'设置表格
With Range("A3:BL" & Range("A" & Rows.Count).End(xlUp).Row)
.Borders.LineStyle = 1 '网格线为实线
.Borders.Color = RGB(221, 221, 221) '网格线颜色
End With
'设置行高
With Range("3:" & Range("A" & Rows.Count).End(xlUp).Row)
.RowHeight = 18 '行间距为18
End With
'设置数字格式
With Range("B6:BL" & Range("A" & Rows.Count).End(xlUp).Row)
.NumberFormatLocal = "0;;;" '数字格式为2位小数,为0时不显示
End With
'设置增长率格式
For i = 1 To 12
Range("B3,C3,E3").Offset(0, (i - 1) * 5).Formula = "=SUBTOTAL(9,OFFSET(B6:B" & Range("A" & Rows.Count).End(xlUp).Row & ",0," & (i - 1) * 5 & "))"
Range("D3").Offset(0, (i - 1) * 5).Formula = "=OFFSET(B3,0," & (i - 1) * 5 & ")/OFFSET(C3,0," & (i - 1) * 5 & ")"
Range("D3").Offset(0, (i - 1) * 5).NumberFormatLocal = "[红色]0.00%↑;[绿色]-0.00%↓;;"
Range("F3").Offset(0, (i - 1) * 5).Formula = "=OFFSET(B3,0," & (i - 1) * 5 & ")/OFFSET(E3,0," & (i - 1) * 5 & ")"
Range("F3").Offset(0, (i - 1) * 5).NumberFormatLocal = "[红色]0.00%↑;[绿色]-0.00%↓;;"
Range("D6:D" & Range("A" & Rows.Count).End(xlUp).Row).Offset(0, (i - 1) * 5).NumberFormatLocal = "[红色]0.00%↑;[绿色]-0.00%↓;;"
Range("D6:D" & Range("A" & Rows.Count).End(xlUp).Row).Offset(0, (i - 1) * 5 + 2).NumberFormatLocal = "[红色]0.00%↑;[绿色]-0.00%↓;;"
Next
Range("D6:D" & Range("A" & Rows.Count).End(xlUp).Row).Offset(0, (i - 1) * 5).NumberFormatLocal = "[红色]0.00%↑;[绿色]-0.00%↓;;"
End Sub