黑色玻璃球:Excel报表汇总通用代码
来源:百度文库 编辑:九乡新闻网 时间:2024/04/29 10:56:47
平时工作中,经常会遇到调查表汇总或者报表汇总的问题。比如近期,作为主管部门,要搞一次整个系统的员工基本情况调查,如果调查单位数量很少,可以通过复制、粘贴的方法,把调查数据复制到同一个工作簿中,然后通过公式进行汇总。可是,如果调查单位有几百个、几千个,并且每张表的数据又很多,使用这种方法就显得非常麻烦。下面的方法可以让你摆脱烦恼,一劳永逸。
第1步:
在E盘根目录下新建一个“报表汇总”文件夹(“报表汇总”文件夹可以建立在任意驱动器的任意文件夹中),并在该文件夹中再新建一个“基层表1”文件夹(各调查单位上报的调查表全部复制到这个文件夹中)。
第2步:
运行Excel,首先把3个工作表的名称分别改为“汇总表”“初始设置”、“过渡表”,然后在工作表“汇总表”中设计所需要的调查表(或报表,下同),并在该工作表中增加一个“汇总”按钮,按钮名称为默认的CommandButton1。设计完成后,保存在“报表汇总”文件夹中,文件名称自定。假如文件名称是“报表汇总1”。
第3步:
根据不同的情况,在“初始设置”工作表的B1:B6单元格中设置不同的内容。其中:“提示内容所在单元格”用于确定提示内容“正在汇总,请稍等。。。”的显示位置。
工作表“过渡表”中没有任何数据,仅仅用于导入各调查单位上报的数据。汇总结束后,该表数据全部清除。
第4步:
在按钮“汇总”的单击事件中粘贴以下代码:
Private Sub CommandButton1_Click() '报表汇总通用代码
Dim TotalUnitNum As Integer, StartRng As String, bDataRows As Byte, bDataColumns As Byte, TipsRng As String
With Sheets("初始设置")
TotalUnitNum = .[B2]: StartRng = .[B3]: bDataRows = .[B4]: bDataColumns = .[B5]: TipsRng = .[B6]
End With
Dim DataPath As String, FileName() As String, iUnitNum As Integer
DataPath = ThisWorkbook.Path & "\" & Sheets("初始设置").Range("B1") & "\"
ReDim Preserve FileName(0)
FileName(0) = Dir(DataPath & "*.xls")
Do While FileName(iUnitNum) <> ""
iUnitNum = iUnitNum + 1
ReDim Preserve FileName(iUnitNum)
FileName(iUnitNum) = Dir
Loop
If iUnitNum <> TotalUnitNum Then '调查表不全
If iUnitNum = 0 Then
MsgBox "无任何调查表数据!", vbOKOnly + vbInformation, "退出"
Exit Sub
End If
If MsgBox("调查表不全,是否强行汇总?", vbYesNo + vbQuestion + vbDefaultButton2, "提示") = vbNo Then Exit Sub
End If
Dim sTmp
sTmp = Range(TipsRng) '保存提示内容所在单元格原有值,这样可把提示内容设在任何单元格中
Range(TipsRng) = "正在汇总,请稍等。。。"
'“过渡表”第 1 行赋值为:行编号、Data1、Data2、Data3、Data4、Data5。。。
Dim i As Integer
With Sheets("过渡表")
.Range("A1") = "行编号"
For i = 1 To bDataColumns
.Cells(1, i + 1) = "Data" & i
Next
End With
'把每个单位的调查表数据导入B2开始的单元格区域中
Dim SourceBook As Object, SourceSheet As Object, Arr, j As Integer, Start As Single
Start = Timer
Application.ScreenUpdating = False
Application.ShowWindowsInTaskbar = False
For i = 0 To UBound(FileName) - 1 '因为最后一个FileName数组为空,所以要-1
Set SourceBook = Workbooks.Open(DataPath & FileName(i), 0, True)
'假如每个单位的调查表数据均在名称为“Sheet1”的工作表中。
On Error GoTo TableErr
Set SourceSheet = SourceBook.Worksheets("Sheet1")
On Error GoTo 0
Arr = SourceSheet.Range(StartRng).Resize(bDataRows, bDataColumns)
SourceBook.Close False
With Sheets("过渡表")
.Range("B" & (2 + i * bDataRows)).Resize(bDataRows, bDataColumns) = Arr
For j = 1 To bDataRows
'每导入一个调查单位的数据,其数据行的“行编号”字段值分别设为R001、R002、R003......等等
'这样,才能使用SQL语言按关键字段“行编号”进行分组统计。
'之所以要增加关键字段“行编号”,是为下面的分组统计的需要。因为下面如果按指标名称进行
'分组统计的话,指标名称可能会有重复。比如,“人员总量”中列出“其中:女性”这一指标,
'“按专业技术职称分”中,也可能有“其中:女性”这一指标。这种情况下按指标名称进行分组
'统计时,统计结果就不正确了。
.Range("A" & (1 + j + i * bDataRows)) = "R" & Right("00" & j, 3)
Next
End With
Next
Set SourceSheet = Nothing
Set SourceBook = Nothing
Dim Cnn As Object, SQL As String
Set Cnn = CreateObject("ADODB.Connection")
'Cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties=Excel 8.0;data Source=" & ThisWorkbook.FullName
Cnn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Extended Properties='Excel 8.0;imex=1;';data Source=" & ThisWorkbook.FullName
'如果数据区域(不包括标题行和指标名称列的区域)中任何一列的前8个单元格均为空白,分组求和的结果都不正确。
'解决办法:要么在Extended Properties中加参数imex=1,要么先把这一列第一个单元格的值改为0。
'在此感谢ExcelHome论坛jyhxr、zhaogang1960等老师的帮助。
SQL = "select "
For i = 1 To bDataColumns
SQL = SQL & "Sum(Data" & i & "),"
Next
'SQL = Left(SQL, Len(SQL) - 1) & " from [过渡表$A1:" & Chr(65 + bDataColumns) & (1 + bDataRows * iUnitNum) & "] group by 行编号"
SQL = Left(SQL, Len(SQL) - 1) & " from [过渡表$A1:" & _
Left(Cells(1, CInt(bDataColumns + 1)).Address(0, 0), Len(Cells(1, CInt(bDataColumns + 1)).Address(0, 0)) - 1) & _
(1 + bDataRows * iUnitNum) & "] group by 行编号"
'Range(StartRng).Resize(bDataRows, bDataColumns).CopyFromRecordset Cnn.Execute(SQL)
Range(StartRng).CopyFromRecordset Cnn.Execute(SQL)
Cnn.Close
Set Cnn = Nothing
Sheets("过渡表").Range("A1").Resize(bDataRows * iUnitNum + 1, bDataColumns + 1).ClearContents
Range(TipsRng) = sTmp
Application.ShowWindowsInTaskbar = True
Application.ScreenUpdating = True
MsgBox "报表汇总结束,用时 " & Round(Timer - Start, 0) & " 秒!", vbOKOnly, "结束"
Exit Sub
TableErr:
Range(TipsRng) = sTmp
MsgBox "工作簿“" & SourceBook.Name & "”中没有需要汇总的工作表," & vbCr & _
"或者,该工作表的名称被用户擅自修改了。" & vbCr & vbCr & _
"请检查后再进行汇总!", vbOKOnly + vbExclamation, "错误提示"
SourceBook.Close False
End Sub
第5步:
为防止调查对象擅自修改表格结构,比如,随意增加或删除行、列,就会导致汇总出来的结果不正确。因此,有必要对工作表设置保护。你可以通过菜单栏进行设置,也可粘贴以下代码到工作表“汇总表”的VBE窗口中,并直接运行“保护工作表”这个过程。
Sub 保护工作表()
Dim StartRng As String, bDataRows As Byte, bDataColumns As Byte, Rng As Range
With Sheets("初始设置")
StartRng = .[B3]: bDataRows = .[B4]: bDataColumns = .[B5]
End With
ActiveSheet.Unprotect Password:="123" '解除对工作表的保护(假设原保护密码是123)
'选中整个表格,添加锁定
Cells.Select
Selection.Locked = True
'选取数据区域,解除锁定
Set Rng = Range(StartRng & ":" & ColumnConv(ColumnConv(Left(StartRng, 1)) + bDataColumns - 1) & (Right(StartRng, 1) + bDataRows - 1))
Rng.Select
Selection.Locked = False
ActiveSheet.Protect Password:="123" '保护工作表,并设置密码为123
ActiveSheet.EnableSelection = xlUnlockedCells '让锁定单元格不能选中
Range(StartRng).Select
End Sub
Function ColumnConv(SpecStr As String) As String
If IsNumeric(SpecStr) Then
ColumnConv = Left(Cells(1, CInt(SpecStr)).Address(0, 0), Len(Cells(1, CInt(SpecStr)).Address(0, 0)) - 1)
Else
ColumnConv = Range(SpecStr & 1).Column
End If
End Function
至此,表格设计及相关代码编写全部结束。
第6步:
制作下发的调查表。把该工作簿复制一份,打开复制的工作簿,删除其中的“初始设置”和“过渡表”两个工作表,并把工作表“汇总表”的名称改为“Sheet1”(代码中认可的工作表名称是Sheet1),删除其中的VBA代码。好了,就把这个工作簿发给调查对象填写吧。
最后再作两点说明:
1、要解除作为汇总表的工作表保护。因为,如果提示内容设置在数据区域以外的单元格中显示,运行时由于有工作表保护会出现错误提示。
2、文件夹的名称以及各单位上报的调查表的名称可随便命名。