Professional Documents
Culture Documents
Using a query as a data source allows you to combine data from multiple tables and also
to format data as you want it to appear in the target worksheet, using data type conversion functions such as CDate, CCur, or CStr.
TIP
With a few clicks, you can resize the worksheet columns as needed, edit the column headers as
needed, and make the column header row bold, and a plain but serviceable worksheet (Figure 3.4)
is ready for use.
FIGURE 3.4
The exported worksheet with a little formatting applied manually.
NEW FEATURE
52
FIGURE 3.5
A dialog form for selecting Northwind Orders data to archive.
FIGURE 3.6
Selecting a date from the calendar pop-up.
Once the start date and end date have been entered or selected, clicking the Archive button runs
a procedure that creates a new Excel worksheet from a template (Orders Archive.xltx) in the same
folder as the database, fills it with data from tblOrders in the selected date range, and deletes the
archived records.
The ArchiveData procedure uses the Start Date and End Date values selected in the dialog as
arguments. This procedure is listed as follows, together with the CreateAndTestQuery procedure it uses to create a query programmatically, and another procedure (TestFileExists) that
tests whether a file exists in a specific folder:
Public Sub ArchiveData(dteStart As Date, dteEnd As Date)
On Error GoTo ErrorHandler
Dim
Dim
Dim
Dim
appExcel As Excel.Application
intReturn As Integer
lngCount As Long
n As Long
53
Part I
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
rng As Excel.Range
rngStart As Excel.Range
strDBPath As String
strPrompt As String
strQuery As String
strSaveName As String
strSheet As String
strSheetTitle As String
strSQL As String
strTemplate As String
strTemplateFile As String
strTemplatePath As String
strTitle As String
wkb As Excel.Workbook
wks As Excel.Worksheet
Create a new worksheet from the template and export the Access data to it:
strDBPath = Application.CurrentProject.Path & \
Debug.Print Current database path: & strDBPath
54
Write data from the recordset to the data area of the worksheet, using the columnoffset argument to move to the next cell:
55
Part I
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
= Nz(rst![OrderID])
rng.Offset(columnoffset:=1)
= Nz(rst![Customer])
rng.Offset(columnoffset:=1)
= Nz(rst![Employee])
rng.Offset(columnoffset:=1)
= Nz(rst![OrderDate])
rng.Offset(columnoffset:=1)
= Nz(rst![RequiredDate])
rng.Offset(columnoffset:=1)
= Nz(rst![ShippedDate])
rng.Offset(columnoffset:=1)
= Nz(rst![Shipper])
rng.Offset(columnoffset:=1)
= Nz(rst![Freight])
rng.Offset(columnoffset:=1)
= Nz(rst![ShipName])
rng.Offset(columnoffset:=1)
= Nz(rst![ShipAddress])
rng.Offset(columnoffset:=1)
= Nz(rst![ShipCity])
rng.Offset(columnoffset:=1)
= Nz(rst![ShipRegion])
rng.Offset(columnoffset:=1)
= Nz(rst![ShipPostalCode])
rng.Offset(columnoffset:=1)
= Nz(rst![ShipCountry])
rng.Offset(columnoffset:=1)
= Nz(rst![Product])
rng.Offset(columnoffset:=1)
= Nz(rst![UnitPrice])
rng.Offset(columnoffset:=1)
= Nz(rst![Quantity])
rng.Offset(columnoffset:=1)
= Nz(rst![Discount])
Save and close the filled-in worksheet, using a workbook save name with the date range selected in
the dialog:
strSaveName = strDBPath & strSheetTitle & .xlsx
Debug.Print Time sheet save name: & strSaveName
56
ChDir strDBPath
On Error Resume Next
Put up a success message, listing the name and path of the new worksheet:
strTitle = Workbook created
strPrompt = Archive workbook & strSheetTitle & _
& vbCrLf & created in & strDBPath
MsgBox strPrompt, vbOKOnly + vbInformation, strTitle
Delete the archived records, processing the many table first, because you cant delete a record in
the one table if there are linked records in the many table:
DoCmd.SetWarnings False
strSQL = DELETE tblOrderDetails.*, _
& tblOrders.ShippedDate _
& FROM tblOrderDetails INNER JOIN qryArchive _
& ON tblOrderDetails.OrderID = qryArchive.OrderID;
Debug.Print SQL string: & strSQL
DoCmd.RunSQL strSQL
strSQL = DELETE tblOrders.* FROM tblOrders WHERE _
& [ShippedDate] Between # & dteStart & # And # _
& dteEnd & #;
Debug.Print SQL string: & strSQL
DoCmd.RunSQL strSQL
57
Part I
ErrorHandler:
Excel is not running; open Excel with CreateObject
If Err.Number = 429 Then
Set appExcel = CreateObject(Excel.Application)
Resume Next
Else
MsgBox Error No: & Err.Number & ; Description:
Resume ErrorHandlerExit
End If
End Sub
Public Function CreateAndTestQuery(strTestQuery As String, _
strTestSQL As String) As Long
This function is called from other procedures to create a filtered query, using a SQL string in its
strTestSQL argument:
On Error Resume Next
Dim qdf As DAO.QueryDef
Delete old query
Set dbs = CurrentDb
dbs.QueryDefs.Delete strTestQuery
On Error GoTo ErrorHandler
Create new query
Set qdf = dbs.CreateQueryDef(strTestQuery, strTestSQL)
Test whether there are any records
Set rst = dbs.OpenRecordset(strTestQuery)
With rst
.MoveFirst
.MoveLast
CreateAndTestQuery = .RecordCount
End With
ErrorHandlerExit:
Exit Function
ErrorHandler:
If Err.Number = 3021 Then
CreateAndTestQuery = 0
Resume ErrorHandlerExit
Else
MsgBox Error No: & Err.Number & ; Description: &
Err.Description
Resume ErrorHandlerExit
End If
58
End Function
Public Function TestFileExists(strFile As String) As Boolean
On Error Resume Next
TestFileExists = Not (Dir(strFile) = )
End Function
The code in the sample database requires a reference to the Excel object library;
Figure 3.7 shows this reference checked in the References dialog, which is opened
from the Tools menu in the Visual Basic window.
NOTE
FIGURE 3.7
Setting a reference to the Excel object model.
After the worksheet of archived records has been created and saved, you will get a message
(depicted in Figure 3.8) listing the location where the archive worksheet was saved.
FIGURE 3.8
A success message after records are archived.
59
Part I
NOTE
See Chapter 7 for a more flexible way of specifying a Templates folder and a Documents
folder.
After the code deletes the archived records first the ones in tblOrderDetails (the many table)
and then those in tblOrders (the one table) a final message appears, as shown in Figure 3.9.
FIGURE 3.9
A final informative message stating that the archived database records have been cleared.
FIGURE 3.10
A worksheet filled with archived Access data.
Saving the newly created worksheet with the xlWorkbookDefault value for the FileFormat
argument saves it as a standard Excel worksheet. If you need to save the worksheet in another format, perhaps for use by someone running an older version of Excel, you can use one of the other
values in the XlFileFormat enum, which are shown in the Object Browser in Figure 3.11. The
xlExcel9795 named constant will create a worksheet in a format usable by people running
Excel 95 or 97. (The worksheet format choices available in VBA code are much more numerous
than those available in the interface, as shown in Figure 3.12.)
60
FIGURE 3.11
Viewing the file format choices for saving an Excel workbook.
If you create a worksheet in the new .xlsx format, only Office 2007 users will be able
to open it. To create a worksheet that can be opened and edited by users with earlier
versions of Office, select one of the other formats. The Excel 97Excel 2003 Workbook (.xls) format
(shown being selected in Figure 3.12) is usable in Office 97 through 2007, so it is generally the most
useful worksheet format.
WARNING
FIGURE 3.12
Selecting a worksheet save format.
61
Part I
To open the Object Browser for examining components of an object model, open the
Visual Basic window and select Object Browser from the View menu, or press F2.
NOTE
CROSS-REF
In this section, data from qryOrdersAndDetails is exported to a new worksheet made from a template and is then formatted in code. For convenience, the ExportNorthwindData procedure
can be run from the macro mcrExportNorthwindData.
The procedure starts by creating a new worksheet from a template (Northwind Orders.xltx), as for
the ArchiveData procedure. Data from the query qryOrdersAndDetails is written to rows
in the worksheet, and then a set of Excel commands is used to apply hairline borders to the data
area, and a double bottom border to the column headings row.
Next, the worksheets data area is sorted by the first two columns (Country and Category), and the
extra values are removed (the effect is similar to turning on Hide Duplicates in an Access report).
Finally, a Grand Total is created under the last row, made large and bold, and enclosed in a box.
The procedure is listed as follows:
Public Sub ExportNorthwindData()
On Error GoTo ErrorHandler
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
62
appExcel As Object
i As Integer
lngCount As Long
lngCurrentRow As Long
lngRows As Long
n As Long
objFind As Object
rng As Excel.Range
rngData As Excel.Range
rngStart As Excel.Range
strCategory As String
strCountry As String
strCurrAddress As String
strDBPath As String
strFormula As String
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
Dim
strPrompt As String
strDataRange As String
strRange As String
strSaveName As String
strSheetName As String
strStartAddress As String
strTemplate As String
strTemplateFile As String
strTitle As String
wkb As Excel.Workbook
wks As Excel.Worksheet
Create a new worksheet from the template and export data to it:
strDBPath = Application.CurrentProject.Path & \
Debug.Print Current database path: & strDBPath
strTemplate = Northwind Orders.xltx
strTemplateFile = strDBPath & strTemplate
If TestFileExists(strTemplateFile) = False Then
63
Part I
Write data from the recordset to cells in the current row of the worksheet, using the columnoffset argument to move to the next cell:
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
Set rng =
rng.Value
= Nz(rst![ShipCountry])
rng.Offset(columnoffset:=1)
= Nz(rst![Category])
rng.Offset(columnoffset:=1)
= Nz(rst![Product])
rng.Offset(columnoffset:=1)
= Nz(rst![Customer])
rng.Offset(columnoffset:=1)
= Nz(rst![OrderID])
rng.Offset(columnoffset:=1)
= Nz(rst![UnitPrice])
rng.Offset(columnoffset:=1)
= Nz(rst![Quantity])
rng.Offset(columnoffset:=1)
= Nz(rst![Discount])
rng.Offset(columnoffset:=1)
= Nz(rst![TotalPrice])
Determine the number of data rows in the worksheet with the UsedRange property:
lngRows = wks.UsedRange.Rows.Count
Debug.Print Number of data rows in worksheet: & lngRows
64
.Borders(xlEdgeLeft).LineStyle = xlContinuous
.Borders(xlEdgeLeft).Weight = xlHairline
.Borders(xlEdgeLeft).ColorIndex = xlAutomatic
.Borders(xlEdgeTop).LineStyle = xlContinuous
.Borders(xlEdgeTop).Weight = xlHairline
.Borders(xlEdgeTop).ColorIndex = xlAutomatic
.Borders(xlEdgeBottom).LineStyle = xlContinuous
.Borders(xlEdgeBottom).Weight = xlHairline
.Borders(xlEdgeBottom).ColorIndex = xlAutomatic
.Borders(xlEdgeRight).LineStyle = xlContinuous
.Borders(xlEdgeRight).Weight = xlHairline
.Borders(xlEdgeRight).ColorIndex = xlAutomatic
.Borders(xlInsideVertical).LineStyle = xlContinuous
.Borders(xlInsideVertical).Weight = xlHairline
.Borders(xlInsideVertical).ColorIndex = xlAutomatic
.Borders(xlInsideHorizontal).LineStyle = xlContinuous
.Borders(xlInsideHorizontal).Weight = xlHairline
.Borders(xlInsideHorizontal).LineStyle = xlContinuous
End With
65
Part I
wks.Range(strDataRange).Select
wks.Sort.SortFields.Clear
wks.Sort.SortFields.Add Key:=Range(strKey1Range), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortNormal
wks.Sort.SortFields.Add Key:=Range(strKey2Range), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortNormal
With wks.Sort
.SetRange Range(strDataRange)
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
66
67
Part I
With appExcel.Selection.Borders(xlEdgeRight)
.LineStyle = xlContinuous
.ColorIndex = 0
.TintAndShade = 0
.Weight = xlMedium
End With
With appExcel.Selection
.Borders(xlInsideVertical).LineStyle = xlNone
.Borders(xlInsideHorizontal).LineStyle = xlNone
End With
Save and close the filled-in worksheet, using a workbook save name with the date range:
strSheetName = Northwind Orders as of _
& Format(Date, d-mmm-yyyy)
Debug.Print Sheet name: & strSheetName
Put up a success message with the name and path of the new worksheet:
strTitle = Workbook created
strPrompt = strSheetName & vbCrLf & created in _
& strDBPath
MsgBox strPrompt, vbOKOnly + vbInformation, strTitle
ErrorHandlerExit:
Exit Sub
ErrorHandler:
Excel is not running; open Excel with CreateObject
68
FIGURE 3.13
A worksheet filled with data and formatted using VBA code.
Summary
When you need to export Access data to Excel worksheets so that everyone who has Office can
work with them, you can use the techniques discussed in this chapter to export Access data in the
interface, or using VBA code, either to a plain default worksheet, or a formatted worksheet created
from an Excel template.
69