AdHoc Report Builder

Tom Heisler, MCSD, and Dennis Hollenbeck, MCSD

You've built every report that management has requested. So what happens the first time they use your product? They want more (or different) reports! Typically, the first thing an advanced user wants is more selection criteria. Here's how to build a table-driven, graphical, reusable Where expression generator that works on any Access report.

Can you predict how your users will want to select data on a report? Typically, every time new selection criteria are allowed, it requires altering a form or adding parameters to a query. You can save yourself a lot of work if you build software flexible enough to allow power users to make their own decisions on which data they want to see in a report. Here's a reusable tool to give users the power to build custom report filters.

Access allows you to filter a report without modifying its design by using a Where expression in the DoCmd.OpenReport method. Our AdHoc Query Builder provides a simple, easy-to-learn interface for building Where expressions. First, we'll show you what the user sees, then we'll show you how it works.

What the user sees
One of the advantages of the AdHoc Query Builder is that a user only has to learn one interface to customize any Access report. The alternative is to have your users build their own ad hoc Where expressions, something that you should only expect from your intermediate and advanced users. At a minimum, when building their own Where clauses, the users must understand how to use logical operators (And, Or) as well as comparison operators (=, <, Is Null, Like).

To create a customized report using our tool, the user first selects a report group from the combo box, then a report from that group in the form's list box. Once a report is selected, the Selection Criteria subform on our form is enabled. The user can then fill out the Selection Criteria grid by specifying the And/Or operator, field name, comparison operator, and value that he or she wants. There's no set limit on the number of criteria that the user can provide. Finally, the user clicks on the Print or Print Preview button to open the report. Figure 1 shows a typical report setup in our AdHoc Query Builder.

Under the hood
The AdHoc Query Builder is table-driven system, so adding a report to the system just consists of adding an entry to the ReportList table. The data in this table fills both the Report Groups combo box and the RepFormat list box on the form. The table requires you to specify the report name, the report group to which the report belongs, and the query used for each report.

When the user selects a report from the RepFormat list box, several things happen. First, the report description appears below the report list box. Next, the RowSource property of the FldName combo box in the Selection Criteria subform is set to the name of the query supplied in the table. This combo box's RowSourceType property has already been set to "Field List." This little-used setting fills the combo box list with the names of fields in the table or query specified in the RowSource property. When a combo box is used in this way, the RowSource can't be a SQL statement. Pressing the Print or Print Preview button on the form calls the function PrintEm, which assembles a Where expression, builds a caption (see the sidebar "Adding Captions to Reports"), and opens the report.

Components
Now we'll take you on a detailed tour of the components involved. There are only five objects to explain: the tables ReportList and tmpReportSelections, the forms ReportDialogAdHoc and ReportDialogAdHocSub, and the module ReportCaption. These components rely on each other, but this whole system is transportable to other MDBs simply by importing these objects.

Table ReportList
This table holds the list of reports available to the AdHoc Query Builder. To add a report, simply add a record to this table. The field ReportGroup is used to fill the combo box ReportGroup. ReportName holds the name of the report object. Together, ReportGroup and ReportName make up the primary key of the table, which allows the same report to show up in more than one group. The ReportQuery field holds the name of the QueryDef or TableDef that will supply the field names for the Selection Criteria subform. ReportDescription is the text the user will see in the list box when selecting a report, and it should be kept short. ReportNotes will show up below the ReportDescription once a report is selected.

Table tmpReportSelections
This table holds the user's selections from the subform ReportDialogAdHocSub. It's purged every time the Ad Hoc form is opened, or when the Clear button is pressed. Each field on the form corresponds to a field in this table.

Form ReportDialogAdHocSub
The user's entries in this subform control how the Where clause passed to the report is to be created. There are five fields in the form:


Form ReportDialogAdHoc
The ReportDialogAdHoc form provides a consistent, easy-to-learn user interface. The table tmpReportSelections is cleared in the Form_Open event handler (pressing the Clear button also clears the table). The ReportGroup combo box contains a list of the distinct ReportGroup values in ReportList. Selecting a report group fires the combo box's AfterUpdate event, which populates the RepList list box with the list of reports. When a report is selected, the field below the list box is updated with the name of the report.

When the user presses either the Print or Preview buttons, the routine PrintEm is called, and the Access constants for print or preview are passed to it as an argument. This allows PrintEm to be used for both print and preview. The method PrintEm builds a Where expression, constructs a report caption, then opens the selected report. With the Where condition built and stored in strWhere, the report name in strDocName and the print mode in intPreview, the call to print the report looks like this:

   DoCmd.OpenReport strDocName, _ 
       intPreview, , sWhere 


Since Access uses different delimiters in SQL statements for different data types -- "'" (single quote) for strings, "#" for dates, and none for numbers -- the AdHoc Query Builder chooses the correct delimiter based on the data type of the field. These delimiters would make the dynamic report caption difficult to read, so they're not included in the caption. Since the first conjunction in the Where expression is ignored, the AdHoc Query Builder strips it off. The PrintEm function then opens the selected report in the view passed into the function as an argument.

Enhancements
The AdHoc Query Builder has been used by our clients for several years, and they all appreciate the ability to tweak their reports without calling us to add selection criteria. Nonetheless, we've thought of some ways to improve the AdHoc Query Builder.

Currently, the AdHoc Query Builder doesn't perform any validation on the contents of the FldValue text box in the subform. Validating this input against the data type would be handy. In addition, the AdHoc Query Builder would be more powerful if users could add their own parentheses to the Where expression. We hope to add those improvements in the future.

Building robust, flexible, reusable software is one of the ways to remain profitable in the software business. The AdHoc Query Builder can be used for almost any set of reports in any Access application, reduces time spent building reports, is easy to maintain, and is fully reusable.

Download REPMNGR.exe

Tom Heisler, MCSD, serves as vice president of n-Dimensional Software, Inc., and has way too much fun. tom@ndimensional.com.

Dennis Hollenbeck, MCSD and reformed Boeing 737 driver, is president of n-Dimensional Software, Inc., an Anchorage, AK-based ISV that produces the Access Upsizer and Access Report Upsizer. dennis@ndimensional.com.


Sidebar: Adding Captions to Reports

When you add flexibility to your reports, it's easy to add confusion as well. To minimize confusion when a user can specify a custom Where expression, it's a good idea to add a dynamic caption to your report that shows the user what selection criteria he or she used.

We originally built our caption builder in Access 2.0, but it works equally well in Access 95 and 97. There are three procedures that we use while building the Where expression for the report that we also use to build a caption. First, we call RPTC_ClearCaption to empty the string variable that we use to hold the caption. As each set of criteria is added, we call RPTC_AppendCaption to add to the caption. In the report, we place a text box at the top of the report to display the caption and set its ControlSource property to "=RPTC_GetCaption()" to retrieve the string containing the caption. Here's the code from the module that holds these routines:

 Option Explicit 
 Private RPTC_ReportCaption As String 
  
 Sub RPTC_AppendCaption(ByVal ToAdd As String) 
   RPTC_ReportCaption = RPTC_ReportCaption & ToAdd 
 End Sub 
  
 Sub RPTC_ClearCaption() 
   RPTC_ReportCaption = "" 
 End Sub 
  
 Function RPTC_GetCaption() As String 
   RPTC_GetCaption = RPTC_ReportCaption 
 End Function 



Sidebar: Foundation Concepts: Where Expressions

The AdHoc Query Builder assembles Where expressions to filter the records in your report. A Where expression is a clause in a query written in Structured Query Language (SQL). A typical SQL query has several parts (some are optional), which must be in this order:


This query has a Select, From, Where, and Order By clause:

 Select PartNo, Qty, Description  
 From Inventory  
 Where Qty = 0 Order By Description; 


This SQL statement would return the part number, quantity, and description for all parts from the inventory table where the quantity was zero. The records would be sorted by description in order from A to Z.

When you open a report in Access, your report will get data from Access using the query stored with the report. The OpenReport method of the DoCmd object allows you to specify a Where expression that limits the records returned by the query. If you specify a Where expression, Access will temporarily insert the expression into the query before it runs.

Here are some more examples of how the Where clause can be used: