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.
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.
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.
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.
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:
- FldConjunction holds the conjunction operator (And, Or). The default is And. The conjunction in the first record is ignored. The conjunction is used to string together all of the criteria that the user enters.
- FldName holds the name of a field in the table or query selected in ReportList. All of the fields available will show up in the combo box and will appear in the exact order they appear in the query or table. The query referenced in the ReportList table doesn’t have to be the same query the report actually uses. As a result, if you want to present the field names in an order different from the one that FldName uses, you can prepare a separate query with the same fields and put the fields in whatever order you want.
- FldType holds a description of the field type and is updated whenever the user selects a field in FldName. We determine the field type by getting a reference to the query pointed to by the FldName’s RowSource (remember, that’s set to the query used by the report). We then use the field name specified in FldName to get a reference to the field that the user has selected. Here’s a code snippet that shows how that works:
Visual Basic123456789101112131415Set qryTmp = dbTmp.QueryDefs(Me!FldName.RowSource)intTmp1 = qryTmp.Fields(Me!FldName).TypeSelect Case intTmp1Case DB_DATE:Me!FldType = "Date"Case DB_TEXT:Me!FldType = "Text"...etc...
- Form ReportDialogAdHoc
- FldComp holds the comparison operator. The AdHoc Query Builder supports =, >, >=, <, <=, Like, Is Null, and Is Not Null. Note that all the comparison operators are available regardless of the field type, though some might not apply to all field types.
- FldValue holds whatever text the user enters. No validation is applied to this text, so if “ABC” is entered for a date, an error will occur when the user attempts to open the report.
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
EnhancementsSince 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.
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.
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:
Private RPTC_ReportCaption As String
Sub RPTC_AppendCaption(ByVal ToAdd As String)
RPTC_ReportCaption = RPTC_ReportCaption & ToAdd
RPTC_ReportCaption = ""
Function RPTC_GetCaption() As String
RPTC_GetCaption = RPTC_ReportCaption
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:
- Select clause — Determines what fields of data to return from the query.
- From clause — Determines the tables from which to extract the data.
- Where clause — Limits the number of records returned.
- Group By clause — Determines how the records are totaled and summarized.
- Order By clause — Determines the order in which the records are returned.
This query has a Select, From, Where, and Order By clause:
Select PartNo, Qty, Description
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:
- Records where Description starts with `A’ will be selected. Note that strings must be enclosed in single or double quotes:
Where Description Like 'A*'
- Records are selected where Sales are greater than or equal to $100,000, or the Country is France, Canada, or USA:
Where ((Country In ('France', 'Canada', 'USA'))
Or (Sales >= 100000))
- Records where the value of the StartDate fields falls between May 12, 1994 and June 30, 1996 inclusive will be selected (that is, a record containing 5/12/1994 would be included in the report). As the code shows, dates must be enclosed in pound signs (#):
Where StartDate Between #5/12/1994# And #6/30/1996#