MS Access
MS Access
MS Access
7.3.6.2 Eliminating unwanted scroll bars and 8.4.1 Why you should never use a combo box
navigation buttons 13 for a non-concatenated key. 19
7.4 Application to the assignment 16 8.4.2 Controls and widgets 21
8.5 Application to the assignment 22
8. Combo Box Controls
8.1 Introduction: What is a combo box? 1 9. Advanced Forms
8.2 Learning objectives 2 9.1 Introduction: Using calculated controls on
8.3 Tutorial exercises 2 forms 1
8.3.1 Creating a bound combo box 2 9.2 Learning objectives 1
8.3.2 Filling in the combo box properties 5 9.3 Tutorial exercises 1
8.3.3 A combo box based on another table or 9.3.1 Creating calculated controls on forms 1
query 6 9.3.2 Showing a total on the main form 2
8.3.3.1 Showing more than one field in the 9.3.2.1 Calculating the aggregate function on
combo box 9 the subform 5
8.3.3.2 Hiding the key field 12 9.3.2.2 Hiding the text box on the subform 9
8.3.3.3 Changing the order of items in the 9.4 Discussion 9
combo box 14 9.5 Application to the assignment 11
8.3.4 Changing a form’s tab order 18
8.4 Discussion 19
having to interact with a large number of “single-per- • macros and Visual Basic programs for extending
sonality” tools, for example: the functionality of database applications.
• Oracle for relational databases All these database objects are stored in a single file
• PowerBuilder for rapid applications development, named <filename>.mdb. When you are running
• SmallTalk for object-oriented programming. Access, a temporary “locking” file named <file-
Keep this advantage in mind as we switch back and name>.ldb is also created. You can safely ignore
forth between personalities and different computing the *.ldb file; everything of value is in the *.mdb file.
paradigms.
1.2 Learning objectives
1.1.2 What is in an Access database
file?
How do I get started?
lection of related data tables, an Access database How do I create or edit a database object?
includes more than just data. In addition to tables, an What is the database window and what does
Access database file contains several different types it contain?
of database objects: How do I import an Excel spreadsheet?
• saved queries for organizing data,
• forms for interacting with the data on screen, How do I delete or rename database objects?
• reports for printing results,
How do I get help from the on-line help Access from the main menu to see which version
system? you are using.
How do I compact a database to save space?
All the screen shots in these tutorials are
taken from Access version 7.0 (released as
1.3 Tutorial exercises part of Office 95). Although there are some
In this tutorial, you will start by creating a new data- important differences between version 2.0
base file. and version 7.0, the concepts covered here
are the same for both. Version 8.0 (released
1.3.1 Starting Access as part of Office 97) is only slightly different
• To start Access, you double click the Access icon from version 7.0.
( for version 8.0 and 7.0 or for version
2.0) from within Microsoft Windows.
If you are working in the Commerce PC Lab, you will
Whenever the instructions given in the tutorial
differ significantly from version 7.0, a warning
box such as this is used.
be working with Access version 2.0. If you are work-
ing at home, you will able be to tell what version you
1.3.2 Creating a new database
are using by watching the screen “splash” as the pro-
gram loads. Alternatively, select Help > About • Follow the directions in Figure 1.1 to create a
new database file called myfile.mdb.
FIGURE 1.1: Select the name and location of your new (empty) database.
New
Create a new database by selecting File >
from the main menu or by clicking the
“new database” button on the tool bar.
Note
Type in a new database name and press Enter.
that you are limited to 8-letter names in
version 2.0.
FIGURE 1.2: The database window contains all the database objects for a particular application.
FIGURE 1.3: Open the univ0_vx.mdb file for the version of Access that you are using and then
open the Sections table
from
Select File > Open Database
the main menu.
You can open a
database object for
viewing, for
modification, or
create a new object.
correct
Select the
file and
open the
Sections
table.
Double-click depts.xls.
Import
Select File > Get External Data >
from the from the main menu
and move the directory containing the
file you want to import. with
Select files of type *.xls (files
that extension will show in
the file window).
FIGURE 1.5: Use the spreadsheet import wizard to import the Excel file.
column
Select the first row contains
headings option so
that the column headings in the
spreadsheet are not interpreted
as data.
about
Since we have not talked
primary keys yet,
select no primary key.
letters
Type in the first few FIGURE 1.6: Use the help system to find
of the topic you information on a specific topic
are looking for.
the
Select the best match from
list (i.e., “compacting
databases”) and double-
click to get a list of topics.
The Index is the best place to For most students, the help
start when you are looking for a system in Access version
specific topic. If you need more 2.0 is easier to navigate.
structured information or are Use the “cue cards” in
looking for an overview, use the version 2.0 to get step-by-
Contents tab. step instructions for many
operations.
FIGURE 1.7: Follow the instructions provided by help to compact your database
used as a substitute for the Save As command. This mended sequence for prototyping using Access is
is especially useful in situations in which you cannot the following:
use the operating system to rename a file (e.g., 1. Model the information of interest in terms of enti-
when you do not have access to the Windows file ties and relationships between the entities (this is
manager). covered in the lecture portion of the course).
2. Create a table for each entity (Tutorial 2).
1.4.4 Developing applications in Access 3. Specify the relationships between the tables
In general, there are two basic approaches to devel- (Tutorial 3).
oping information systems: 4. Organize the information in your tables using
• in-depth systems analysis, design, and imple- queries (Tutorial 4, Tutorial 5, Tutorial 10)
mentation, 5. Create forms and reports to support input and
• rapid prototyping (in which analysis, design, and output transactions (Tutorial 6, Tutorial 7).
implementation are done iteratively) 6. Enhance you forms with input controls
Access provides a number of features (such as (Tutorial 8)
graphical design tools, wizards, and a high-level 7. Create action queries (Tutorial 11), macros
macro language) that facilitate rapid prototyping. (Tutorial 13), or Visual Basic programs
Since you are going to build a small system and (Tutorial 12, Tutorial 14) to perform the transac-
since time is limited, you will use a rapid prototyping tion processing functions of the application.
approach to build your application. The recom-
8. Create “triggers” (procedures attached to events) machine, update the links to the data file, and the
to automate certain repetitive tasks (Tutorial 15). upgrade is done.
1.4.5 Use of linked tables Do not used linked tables in the assignment.
The links are dependent on the absolute
Most professional Access developers do not put their
directory structure. As a result, if the directory
tables in the same database file as their queries,
structure on your machine is different from
forms, reports, and so on. The reason for this is sim-
that on the marker’s machine, the marker will
ple: keep the application’s data and interface sepa-
not be able to use your application without
rate.
first updating the links (a time consuming pro-
Access allows you to use the “linked table” feature to cess for a large number of assignments).
link two database files: one containing all the tables
(“data”) and another containing all the interface and 1.5 Application to the assignment
logic elements of the application (“interface”). The
After completing this tutorial you should be ready to
linked tables from the data file show up in the inter-
face file with little arrows (indicating that they are not create the database file that you will use for the
remainder of the course.
actually stored in the interface file).
1. Create an empty database file called <your
In this way, you can modify or update the interface
file without affecting the actual data in any way. You groupID>.mdb. Remember that your group
number consists of eight digits.
just copy the new interface file over to the user’s
tions, default values, constraints, etc.) to be stored at How do I specify field properties such as the
the table level. input mask and caption?
ofAddthea “new
new record by clicking in the DeptCode field
record” field (marked by the asterisk).
It is seldom necessary to
explicitly save new
records (or changes to
existing records) since
Access automatically
saves whenever you
move to another record,
close the table, quit
Access, etc.
data,
To permanently save the change to the
click on the record selector (note the
icon changes from a pencil to a triangle).
create
Click the New button to
a new table.
the
Select “design view” (avoid using
table wizard at this point).
FIGURE 2.4: Use the table design window to enter the field properties for the Employees table.
data
Enter the field names and
types for the five fields.
FIGURE 2.5: Set the primary key for the Employees table.
fields)
Click on the grey box beside the field (or
that form the primary key.
select
Either click the key-shaped icon in the tool bar or
Edit > Primary Key from the menu.
• Select View > Datasheet from the main menu to three small dots ( ) to invoke the input mask
switch to datasheet mode as shown in Figure 2.7. wizard.
Enter your own S.I.N. and observe the effect of • Follow the instructions provided by the wizard as
the input mask and caption on the EmployeeID shown in Figure 2.8.
field. • Press F1 while the cursor is still in the input mask
• Select View > Table Design from the main menu property. Scroll down the help window to find the
to return to design mode. meaning of the “0”, “9”, “>” and “L” input mask
• Set the field properties for FName and LName symbols.
(note that Length and Caption are the only two
properties that are relevant for these two fields) 2.4 Discussion
2.3.5 Using the input mask wizard 2.4.1 Key terminology
In this section, you will use the input mask wizard to A key is one or more fields that uniquely determine
create a complex input mask for a standard field the identity of the real-world object that the record is
type. You will also use the help system to learn more meant to represent. For example, there is a record in
about the meaning of the symbols used to create the student information system that contains infor-
input masks. mation about you as a student. To ensure that the
• Select the Phone field, move the cursor to the record is associated with you and only you, it con-
input mask property, and click the button with
2. Tables Discussion
FIGURE 2.7: Observe the effect of the input mask and caption properties on the behavior of the
EmployeeID field during data entry
numbers
Try entering various characters and
into the EmployeeID
field.
If a caption is specified, it replaces the
done
Press the Escape key when you are
to clear the changes to the record.
field name in the field selector.
Note that the input mask will not let you
type any characters other than numbers
from 0-9. In addition, the spaces between
the groups of numbers are added
automatically.
Input masks provide a relatively easy way to
avoid certain basic data input errors without
having to write complex error checking
programs. Note, however, that it is possible to
over-constrain a field so that users are unable to
enter legitimate values.
FIGURE 2.8: Use the input mask wizard to create an input mask.
number”
Select “phone
from the
list of commonly-
used field types.
2. Tables Discussion
tains a field called “student number” that is guaran- nated key is made by joining together two or
teed to be unique. more fields. Course numbers at UBC provide a
The advantage of using student number as a key good example of a concatenated key made by
instead of some other field—like “student name”—is joining together two fields: DeptCode and
that there may be more than one person with the CrsNum. For example, department alone cannot
same first and last name. The combination of stu- be the primary key since there are many courses
dent name and address is probably unique (it is in each department (e.g., COMM 335, COMM
improbable that two people with the same name will 391). Similarly, course number cannot be used as
at the same address) but using these two fields as a a key since there are many courses with the
key would be cumbersome. same number in different departments (e.g.,
COMM 335, HIST 335, MATH 335). However,
Since the terminology of keys can be confusing, the
department and course number together form a
important terms are summarized below.
concatenated key (there is only one COMM 335).
1. Primary key — The terms “key” and “primary 3. Foreign key: In a one-to-many relationship, a
key” are often used interchangeably. Since there foreign key is a field (or fields) in the “child”
may be more than one candidate key for an record that uniquely identifies the correct “parent”
application, the designer has to select one: this is record. For example, DeptCode and CrsNum in
the primary key. the Sections table are foreign keys since these
2. Concatenated key: The verb “concatenate” two keys taken together are the primary key of
means to join together in a series. A concate-
the Courses table. Foreign keys are identified in In addition, you can use the comment field in the
Access by creating relationships (see Tutorial 3). table design window to document the meaning of
field names.
2.4.2 Fields and field properties
It is strongly recommended that you avoid all
2.4.2.1 Field names
non-alphanumeric characters whenever you
Access places relatively few restrictions on field name a field or database object. Although
names and thus it is possible to create long, descrip- Access will permit you to use names such as
tive names for your fields. The problem is that you Customer#, non-alphanumeric characters
have to type these field names when building que- (such as #, /, $, %, ~, @, etc.) may cause
ries, macros, and programs. As such, a balance undocumented problems later on.
should be struck between readability and ease of
typing. You are advised to use short-but-descriptive 2.4.2.2 Data types
field names with no spaces. The field's data type tells Access how to handle the
For example, in Section 2.3.2 you created a field information in the field. For instance, if the data type
with name FName. However, you can use the caption is date/time, then Access can perform date/time
property to provide a longer, more descriptive label arithmetic on information stored in the field. If the
such as First name. The net result is a field name same date is stored as text, however, Access treats
that is easy to type when programming and a field it just like any other string of characters. Normally,
caption that is easy to read when the data is viewed.
2. Tables Discussion
the choice of data type is straightforward. However, for use as a primary key when no other key is
the following guidelines should be kept in mind: provided or is immediately obvious.
1. Do not use a numeric data type unless you are
Since an autonumber is really Long Integer
going to treat the field as a number (i.e., perform
and since relationships can only be created
mathematical operations on it). For instance, you
between fields with the same data type, it is
might be tempted to store a person's student
important to remember that if an autonumber
number as an integer. However, if the student
is used on the “one” side of a relationship, a
number starts with a zero, then the first digit is
long integer must be used for the “many” side.
dropped and you have to coerce Access into dis-
playing it. Similarly, a UBC course number (e.g., 2.4.2.3 “Disappearing” numbers in
335) might be considered a number; however, autonumber fields
since courses like 439B have to accommodated,
If, during the process of testing your application, you
a numeric data type for the course number field is
add and delete records from a table with an auto-
clearly inappropriate.
number key, you will notice that the deleted keys are
2. Access provides a special data type called Auto
not reclaimed.
Number (Counter in version 2.0). An autonum-
ber/counter is really a number of type Long Inte- For instance, if you add records to your Customer
ger that gets incremented by Access every time table (assuming that CustID is an autonumber), you
a new record is added. As such, it is convenient will have a series of CustID values: 1, 2, 3… If you
later delete customer 1 and 2, you will notice that lutely no difference whether the first customer in your
your list of customers now starts at 3. customers table is CustID = 1 or 534.
Clearly, it would be impossible for Access to renum- 2.4.2.4 Input masks
ber all the customers so the list started at 1. What An input mask is a means of restricting what the user
would happen, for instance, to all the printed
can type into the field. It provides a “template” which
invoices with CustID = 2 on them? Would they refer tells Access what kind of information should be in
to the original customer 2 or the newly renumbered
each space. For example, the input mask >LLLL
customer 2? consists of two parts:
The bottom line is this: once a key is 1. The right brace > ensures that every character
assigned, it should never be reused, even if the user types is converted into upper case.
the entity to which it is assigned is subse- Thus, if the user types comm, it is automatically
quently deleted. Thus, as far as you are con- converted to COMM.
cerned, there is no way to get your customers 2. The characters LLLL are place holders for letters
table to renumber from CustID = 1. from A to Z with blank spaces not allowed. What
this means is that the user has to type in exactly
Of course, there is a long and complicated way to do four letters. If she types in fewer than four or
it, but since used an autonumber in the first place, types a character that is not within the A to Z
you do not care about the actual value of the key— scope (e.g., &, 7, %), Access will display an error
you just want it to be unique. In short, it makes abso- message.
2. Tables Discussion
There are a large number of special symbols used The semicolon and zero at the end of this input mask
for the input mask templates. Since the meaning of are important because, as the on-line help system
many of the symbols is not immediately obvious, points out, an input mask value actually consists of
there is no requirement to remember the character three parts (or “arguments”), each separated by a
codes. Instead, simply place the cursor on the input semicolon:
mask property and press F1 to get help. In addition, • the actual template (e.g., 000\-0000),
the wizard can be used to provide a basic input mask • a value (0 or 1) that tells Access how to deal with
which can later be modified. literal characters, and
2.4.2.5 Input masks and literal values • the character to use as a place holder (showing
the user how many characters to enter).
To have the input mask automatically insert a char-
When you use a literal character in an input mask,
acter (such as a space or a dash) in a field, use a
slash to indicate that the character following it is a lit- the second argument determines whether the literal
value is simply displayed or displayed and stored in
eral.
the table as part of the data.
For example, to create an input mask for local tele-
For example, if you use the input mask 000\-
phone numbers (e.g., 822-6109), you would use the
following template: 000\-0000;0 (the dash is a lit- 0000;1, Access will not store the dash with the tele-
phone number. Thus, although the input mask will
eral value and appears automatically as the user
enters the telephone number). always display the number as “822-6109”, the num-
ber is actually stored as “8226109”. By using the
input mask 000\-0000;0, however, you are telling 2.5 Application to the assignment
Access to store the dash with the rest of the data.
You now have the skills necessary to implement your
If you use the wizard to create an input mask, tables.
it asks you a simple question about storing lit- • Create all the tables required for the assignment.
eral values (as shown in Figure 2.8) and fills • Use the autonumber data type (counter in version
in the second argument accordingly. How- 2.0) for your primary keys where appropriate.
ever, if you create the input mask manually, • Specify field properties such as captions, input
you should be aware that by default, Access mask, and defaults where appropriate.
does not store literal values. In other words,
If you create an input mask for ProductID,
the input mask 000\-0000 is identical to the
ensure you understand the implications of
input mask 000\-0000;1. This has impor-
Section 2.4.2.5.
tant consequences if the field in question is
subject to referential integrity constraints (the • Set the Default property of the OrderDate field
value “822-6109” is not the same as so that the current date is automatically inserted
“8226109”). into the field when a new order is created (hint:
see the Date() function in the on-line help sys-
tem).
FIGURE 3.1: The “monolithic” approach to database design—the Catalog View table contains
information about courses and sections.
to the same record in the Courses table so the • Select Tools > Relationships from the main
course information only needs to be stored once. menu.
FIGURE 3.3: Add the Courses and Sections tables to the relationship window.
To select a concatenated
key (more than one If done
field) hold down the correctly, the
Control key while connectivity (1
selecting. to ∞) shows on
the relationship
line(s).
Ensure that the correct
fields are associated
with each other (this
must be done manually
for concatenated keys).
enforce
Check the box to
referential
integrity.
3.3.2 Editing and deleting relationships Note that simply deleting the table in the rela-
There are two common reasons for having to edit or tionship window does not delete the relation-
delete a relationship: ship, it merely hides it from view.
3. Relationships Discussion
click
With the relationship selected, right-
to get the edit/delete pop-up
menu. If you do not get this menu,
make sure you have correctly
selected the relationship.
Although the data modeling technique used most “many” side of a relationship has a corresponding
often in information system development—Entity- record on the “one” side.
Relationship diagraming—permits the specifica- Enforcing referential integrity means that you cannot,
tion of many-to-many relationships, these relation- for instance, create a new record in the Sections
ships cannot be implemented in a relational table without having a valid record in the Courses
database. As a consequence, many-to-many rela- table. This is because having a section called
tionships are usually broken down into a series of “BSKW 101 Section 001” is meaningless unless
one-to-many relationships via “composite entities” there is a course called “BSKW 101”. In addition, ref-
(alternatively, “bridging tables”). Thus to implement erential integrity prevents you from deleting records
the student-takes-course relationship, three tables on the “one” side if related records exist on the
are used: Students, Courses, and Student- “many” side. This eliminates the problem of
TakesCourse. “orphaned” records created when parent records are
deleted.
3.4.2 Referential integrity
Referential integrity is especially important in the
One important feature of Access is that it allows you context of transaction processing systems. Imagine
to enforce referential integrity at the relationship that someone comes into your store, makes a large
level. What is referential integrity? Essentially, refer- purchase, asks you to bill customer number “123”,
ential integrity means that every record on the and leaves. What if your order entry system allows
you to create an order for customer “123” without
first checking that such a customer exists? If you A primary key and a foreign key must be of
have no customer 123 record, where do you send the same data type before a relationship can
the bill? be created between them. Because of this, it
In systems that do not automatically enforce referen- is important to remember that the autonumber
tial integrity, these checks have to be written in a pro- data type (or counter in version 2.0) is really a
gramming language. This is just one example of how long integer.
table-level features can save you enormous pro-
It never makes sense to have a relationship
gramming effort.
between two autonumber fields. A foreign key
Enforcing referential integrity has obvious cannot be an autonumber since referential
implications for data entry: You cannot popu- integrity constraints require it to take on a an
late the “many” side of the table until you pop- existing value from a parent table.
ulate the “one” side.
Queries address this problem. They allow the user to How do I create a query?
join data from one or more tables, order the data in What can I do with a query?
different ways, calculate new fields, and specify cri-
teria to filter out certain records.
How do I create a calculated field?
the
Select the Queries tab in
database window.
create
Press the New button to
a new query.
FIGURE 4.2: Add tables to your query using the “show table” window.
byAddselecting
the Courses table to the query
it and pressing Add
(alternatively, you can simply double-
click on the table you want to add).
table”
Press Close when done (the “show
window is “modal”—you can
The “show table” window is always
available from the Query > Show Table
not do anything else in Access until a menu. Alternatively, you can press the
modal window is closed). “show table” button on the tool bar.
FIGURE 4.4: Project a subset of the available fields into the query definition.
drag
Select the field you wish to project and
it into the query definition grid.
Alternatively, double-click the field.
and
Select “ascending” for the DeptCode field
“descending” for the CrsNum field.
FIGURE 4.6: Select a subset of records from the Courses table matching a specific criterion.
matching
View the results. Only records
the criteria are shown.
• Perform the steps shown in Figure 4.8 to create a • Project Title from the Courses table and
query giving the following result: DeptCode, CrsNum, Section and Catalog-
“Show the department, course number, and title Num from the Sections table (see Figure 4.9).
of all courses from the Commerce department • Follow the instructions in Figure 4.10 to move
and also show those from the Creative Writing CatalogNum to the far left of the query definition
department for which the number of credits is grid.
greater than three.” Access performs an automatic lookup of information
4.3.2.5 Joining from the “one” side of the relationship whenever the
In Tutorial 3, you were advised to break you informa- a valid value is entered into the foreign key of the
“many” side of the relationship. To see how this
tion down into multiple tables with relationships
between them. In order to put this information back works, create a new section of “MUSC 105”:
together in a usable form, you use a join query. • Scroll to the bottom of the query in datasheet
• Close qryCourses. mode and click on the department field.
• Open the relationships window and ensure you • Enter “MUSC”.
have a relationship defined between Courses • Enter “105” in the course number field.
and Sections. If you do not, create one now (do Once Access knows the DeptCode and CrsNum of
not forget to enforce referential integrity). a section, it can uniquely identify the course that the
• Create a new query called qryCatalogNum section belongs to (which means it also knows the
based on the Courses and Sections tables. values of Title, Credits, Activity, etc.)
criterion
Enter the Credits
in the
DeptCode
Enter the second row.
criteria in
different rows.
Note
Bring Courses and Sections into the query.
that the relationship between the tables is
inherited from the relationship window.
the
Project fields from both tables into
query definition.
“column
Click once on the grey
selector”
above the field you
want to move (if
properly selected, the
column turns black).
itsDragnewthelocation.
selected column to
4.3.3 Creating calculated fields the expression involves two fields from the Courses
A calculated field is a “virtual field” in a query for table (DeptCode and CrsNum) and the ampersand
which the value is a function of one or more fields in operator (see Section 4.4.2 for more information on
the underlying table. To illustrate this, we will create using the ampersand operator).
two calculated fields: • Create a new query called qryCourseLengths
based on the Courses table.
1. one to combine DeptCode and CrsNum into one
• Follow the instructions in Figure 4.11 to create
field,
the calculated field Course
2. one to translate the Credits field into a dichoto-
• Run the query to verify the results, as shown in
mous string variable (full year or half
Figure 4.12.
year).
The syntax of a calculated field is always the same: When you use field names in expressions,
<calc field name>: <definition> Access normally adds square brackets. This
For example, the syntax for the calculated field is not cause for concern because in Access,
called Course is: square brackets simply indicate the name of a
Course: DeptCode & CrsNum field (or some other object in the Access envi-
ronment). However, if your field name con-
The calculated field name can be just about any-
tains blank spaces (e.g., Dept Code), the
thing, as long as it is unique. The definition is any
square brackets are NOT optional—you must
expression that Access can evaluate. In this case,
The zoom window provides more room to type than the tiny
space in the query definition grid. Invoke the zoom window
by moving to the area of the grid in which you wish to type
and either right-click or press the Shift-F2 keys.
the
Put the cursor in
Field row of
the first column
and invoke the
zoom window.
and
Type in the name
the definition Press OK when you
of the calculated have finished typing
field. The name the expression.
cannot be the same
as that of an
existing field.
type them every time you use the field name three credits is a full-year course). To do this, we will
in an expression. use the “immediate if” (iif) function.
• Search on-line help for information about the
4.3.3.1 Refining the calculated field iif() function.
Instead of having DeptCode and CrsNum run Basically, the function uses the following syntax:
together in the new Course field, you may prefer to
iif(<expression>, <true part>,
have a space separating the two parts. <false part>)
• Edit the Courses field by clicking on the field row
to implement the following logic:
and invoking the zoom box.
IF <expression> = TRUE THEN
• Add a space (in quotation marks) between the
RETURN <true part>
two constituent fields:
ELSE
Course: DeptCode & ” ” & CrsNum
RETURN <false part>
• Switch to datasheet mode to see the result.
END IF
4.3.3.2 A more complex calculated field • Create a new calculated field called Length:
To create a calculated field that maps Credits to a Length: iif(Credits > 3, “full
dichotomous string variable, we need a means of year”, “half year”)
testing whether the value of Credits exceeds a • Verify the results, as shown in Figure 4.13.
certain threshold (e.g., any course with more than
FIGURE 4.13: Create a calculated field using the “immediate if” function
Length:
Create a calculated field called Length with the following expression:
iif(Credits>3, “full year”, “half year”)
• Use capitalization rather than spaces to separate • Stick to standard alphanumeric characters — You
words — Unlike many database systems, Access should limit yourself to the characters [A...Z],
allows spaces in object names. However, if you [a...z], [0...9], and perhaps underscore (_) and
choose to use spaces, you will have to enclose dash (-). Although Access allows you to use virtu-
your field names in square brackets whenever ally any character, undocumented problems have
you use them in expressions (e.g., [Back been encountered in the past with non-alphanu-
Orders]). As such, it is slightly more efficient to meric characters such as the pound sign (#).
use a name such as BackOrders than Back Table 4.1 shows a suggested naming convention for
Orders. Access database objects (you will discover what
• Give each type of object a distinctive prefix (or these objects are in the course of doing the tutorials).
suffix) — This is especially important in the con-
text of queries since tables and queries cannot 4.4.2 The ampersand (&) operator
have the same name. For example, you cannot
The ampersand operator is like any other operator
(e.g., +, -, ×, ÷) except that it is intended for use on
have a table named BackOrders and a query
named BackOrders. However, if all your query
strings of characters. What the ampersand does is
names are of the form qryBackOrders, then
simply add one string on to the end of another string
distinguishing between tables and queries is
(hence its other name: the “concatenation” operator).
straightforward.
For example, the expression
“First string” & “Second string”
DeptCode
Change the source table for
and CrsNum
from Sections to
Courses.
FIGURE 4.16: The result of attempting to save a record in which the foreign key is missing
new
Attempt to save the
section by
clicking its record
selector.
view
Project fields from both tables and
the query in datasheet mode
(i.e., view the “recordset”).
Attempt to
change a value in
the recordset.
CrsNum
To create a nonsensical query, delete the
relationship by clicking on it Note the absence of the asterisk and the “new record”
and pressing the Delete key. Leave the row. This is a sure sign that the recordset is non-updatable.
DeptCode relationship intact.
the values in this recordset would create anomalies, should appear automatically. If they do not, see
Access designates the recordset as non-updatable. Section 4.4.3.
• Create a calculated field in your qryOrderDe-
A common mistake is to build data entry tails query that calculates the extended price
forms on nonsensical queries and to assume (quantity shipped × price) of each order detail.
that there is a mistake in the form when the • Enter the first order into your system by entering
forms do not work. Clearly, if a query is non- the information directly into tables or queries.
updatable, a form based on the query is also This involves creating a single Orders record
going to be non-updatable. A quick check for and several OrderDetails records. You must
a “new record” row in the query can save time also consult the Products and BackOrders
and frustration. tables to determine the quantity of each item to
ship.
4.5 Application to the assignment
• Create a query to sort the Products table by Entering orders into your system will be much
ProductID. less work once the input forms and triggers
• Create a query that joins the OrderDetails are in place. The goal at this point is to get
and Products tables. When you enter a valid you thinking about the order entry process
ProductID, the information about the product and ways in which it can be automated.
(such as name, quantity on hand, and so on)
AND Courses.DeptCode =
Sections.DeptCode
WHERE Courses.DeptCode=”COMM”;
5.4 Discussion
Although the syntax of SQL is not particularly diffi-
cult, writing long SQL queries is tedious and error-
prone. For this reason, you are advised to use QBE
for the assignment.
In the real world, however, when you say you know
something about databases, it usually implies you
know the “data definition” and “data manipulation”
aspects of SQL in your sleep. If you plan to pursue a
career in information systems, a comprehensive
SQL reference book can be a worthwhile investment.
How do I make the contents of a field on a • Create a new blank form based on the Courses
form read-only? table, as shown in Figure 6.2.
What is an unbound text box? How do I create • The basic elements of the design screen are
one? shown in Figure 6.3. Use the View menu to dis-
FIGURE 6.2: Create a new form to display data from the Courses table.
the
Select the Forms tab from
database window.
use
Select Design View (do not
the wizard at this point)
Bind the form to the
Courses table.
The field list — shows the fields The toolbox — the icons in the If the field list and toolbox
in the table or query to which the toolbox are used to create graphical are not displayed, use the
form is bound. items and controls on the form. View menu or toolbar icons.
FIGURE 6.4: Create a bound text box for the DeptCode field.
Access uses the field’s caption property as the default label for the text box.
If no caption is specified, the field name (e.g., DeptCode) is used. To save
time editing labels, choose your captions with this feature in mind.
• Drag the remaining fields on to the form, as • Scroll down the property sheet to the Locked
shown in Figure 6.5 (do not worry about whether property and set it to Yes, as shown in
the fields are lined up perfectly). Figure 6.7.
• Select View > Form to see the resulting form. • Switch to the form view and attempt to change
Alternatively, press the form view icon ( ). the contents of the DeptCode field.
• Select View > Form Design or press the design A stronger form of protection than locking a field is
view icon ( ) to return to design mode. “disabling” it.
6.3.1.2 Using a field’s properties to protect its • Return to design mode and make the following
contents changes: reset the Locked property to No; set the
Every object on an Access form (e.g., text box, label, Enabled property to No.
• Attempt to change the contents of the DeptCode
detail section, etc.) has a set of properties that can
be modified. In this section, you are going to use the field in form view, as shown in Figure 6.8.
• Save the form as frmCourses.
Locked and Enabled properties to control the user’s
ability to change the information in a field. 6.3.1.3 Adding an unbound text box
• Select the DeptCode text box and right-click to All the text boxes created in the previous section
bring up its property sheet, as shown in were “bound” text boxes—that is, they were bound to
Figure 6.6. a field in the underlying table or query. When you
change the value in a bound text box, you are mak-
FIGURE 6.5: Add the text boxes and switch to form view to see the resulting form.
fields
Add the remaining
to the form.
main
Select View > Form from the
menu to view the form.
You can add more than one field to the form with one
drag-and-drop operation by holding down the Control
button when selecting the fields from the field list.
FIGURE 6.6: Bring up the property sheet for the DeptCode text box.
object
Right-click once on the selected
to get the pop-up menu.
DeptCode
Select the object (e.g., the
text box) for
The properties are broken down
into four groups. To see all the
which you wish to see the properties, select the All tab.
properties. When an object
has been selected, it is
bordered by six dark
“handles”. Some properties of the text box (such as
input mask) are inherited from the field
to which the text box is bound.
FIGURE 6.8: Set the Enabled property of DeptCode to No and attempt to change the value in the
field.
Enabled
Set Locked to No and
to No.
FIGURE 6.10: Set the Control Source property FIGURE 6.11: Create a new form using the form
of an unbound text box. wizard.
wizard.
Select the form
Courses
Bind the form to the
table.
the
Use the pull-down list to set
Control Source property
to DeptCode.
FIGURE 6.12: Use the form wizard to determine the order of fields on your form.
The primary advantage of the wizard is that it auto- look and behavior of the data. The three different
matically creates, formats, and aligns the bound text types of forms are shown in Figure 6.13.
boxes. Of course, once the wizard has created a
form, you are free to modify it in any way. 6.5 Application to the assignment
• Use the wizard to create columnar forms for all
If you make a mistake when creating a form your master tables. Note that in some cases
(e.g., you put the fields in the wrong order) it (e.g., BackOrders) you will want to base the
is often easier to use the wizard and start over form on a join query rather than table in order to
than to fix the problem manually. show important information such as CustName
and ProductName.
6.4 Discussion
6.4.1 Columnar versus tabular versus
datasheet forms
Columnar forms show one record per page. Tabular
forms, in contrast, show many records per page and
are used primarily as subforms. There is also a a
datasheet form type, but it is seldom used since it
gives the developer relatively little control over the
FIGURE 6.13: The same information displayed as a columnar, tabular, and datasheet form.
A tabular form
displays more than
one record per page.
1. create and save both forms (one columnar, one space they occupy. A number of editing issues
tabular) separately; are highlighted in Figure 7.5.
2. drag the subform on to the main form; and, • Save the form as sfrmSections and close it.
3. verify the linkage between the two forms.
7.3.3 Linking the main form and subform
7.3.1 Creating the main form In this section, you are going to return to the main
• Use the wizard to create a columnar form based form and drag the saved subform from the database
on the Courses table. window to an appropriate position on the main form.
• Rearrange the fields so that they make efficient • Open the main form (frmCoursesMain) in
use of the top part of the form, as shown in design mode.
Figure 7.2. • Select Window > univ0_vx: Database to open the
• Save the form as frmCoursesMain. database window in the foreground. Alternatively,
you can press the database window icon ( ) on
7.3.2 Creating the subform the tool bar.
• Use the wizard to create the subform, as shown • Perform the steps shown in Figure 7.6 to drag the
in Figure 7.3 and Figure 7.4. subform on to the main form.
• Subforms created by the wizard typically require • The result of the drag-and-drop operation are
some fine tuning in order to reduce the amount of shown in Figure 7.7. The advantage of the drag-
and-drop method of creating a sub form is that
FIGURE 7.2: Rearrange the text boxes on the main form to make room for the subform.
columnar
Use the wizard to create a
form based on
Courses.
rearrange
Enter form design mode and
the text boxes to
make room for the subform.
FIGURE 7.3: Use the wizard to create the Sections subform (part 1).
DeptCode
There is no need to include
and CrsNum since they
are shown in the main form.
new
Select the form wizard and bind the
form to the Sections table.
FIGURE 7.4: Use the wizard to create the Sections subform (continued)
form,
Since a subform is embedded in a main
you do not have to provide a title.
FIGURE 7.5: Edit the subform to reduce the amount of space it uses.
“detail
Reduce the vertical space by moving the fields up to the
band” and bringing the “form footer” band up
byReduce the horizontal space used
the headings and fields.
against the fields (to move a band, drag it using the mouse).
window
Position the database
so that the
subform’s target
destination is visible.
the width of the subform control (the white win- Since both the forms created in Section 7.3.3 were
dow) is automatically set to equal the width of the built on tables, Access could automatically deter-
subform. mine the relationship.
• Verify the link between the form and the subform
If you make changes to the size of your sub- by examining the property sheet of the subform
form once the subform control is created, you control, as shown in Figure 7.8.
may have to resize the subform control by
clicking and dragging a corner handle. The terminology “link child field” and “link
master field” is identical to “foreign key” and
7.3.4 Linking forms and subforms “primary key”. The main form is the parent
manually (“one” side) and the subform is the child
If both the form and the subform are based on (“many” side).
tables, and if relationships have been defined
• View the resulting form. Notice that as you move
between the tables, Access normally has no problem
from course to course, the number of sections
determining which fields “link” the information on the
shown in the subform changes (see Figure 7.9).
main form with the information in the subform. How-
ever, when the forms are built on queries, Access
has no relationship information to rely on. As such,
you have to specify the form/subform links manually.
The form footer is pushed down when the subform control is created. You
may move the footer to create more or less area at the bottom of the form.
control
Select the Sections subform
(the white window) and bring
up its property sheet.
determined
Verify that Access has correctly
the link fields.
290,
Note that for COMM
eight courses are For COMM 291, four
sections are listed in
listed in the subform. the subform.
record”
Click the “next
navigation
button on the main
form to move to the
next course.
7.3.5 Non-synchronized forms itself) to make your form more attractive and easier
In this section, you will delete the link fields shown in to use.
Figure 7.8 in order to explore some of the problems In Figure 7.11, the basic form created in the previous
associated with non-synchronized forms. sections is shown and a number of shortcomings are
• Return to form design mode and delete the link identified.
fields (highlight the text and press the Delete 7.3.6.1 Changing the form’s caption
key). • Select the form as shown in Figure 7.12.
• View the form. Note that all records in the Sec- • Change its Caption property to “Courses and
tions table (not just those associated with a Sections”.
particular course) are shown.
• Attempt to add a new section to COMM 290 as 7.3.6.2 Eliminating unwanted scroll bars and
shown in Figure 7.10. navigation buttons
• Re-establish the correct link fields and save the Scroll bars and navigation buttons are also form-
form. level properties. However, in this case, you need to
modify the properties of the subform.
7.3.6 Aesthetic refinements • To quickly open the subform in design mode,
In this section, you will modify the properties of sev- double-click the subform control when viewing
eral form objects (including the properties of the form the main form in design mode (this takes some
practice)
fields
Delete the link
for the
subform control
and view the
resulting form.
sections
Note that all 37
show in
the subform
(moving to a
different course
has no effect).
Add a new
catalog number
and click the
record selector Since the forms are not synchronized, the
to try to save the DeptCode and CrsNum fields of the Sections
new record. table are not automatically filled in by Access.
It is important to realize that combo boxes What is tab order? How do I change it so that
have no intrinsic search capability. Combo the cursor moves in the correct order?
boxes change values—they do not automati- Should I put a combo box on a key field?
cally move to the record with the value you
select. If you want to use a combo box for 8.3 Tutorial exercises
search, you have to program the procedure
• Open your frmCourses form in design mode.
yourself (see Tutorial 15 for more details). • Ensure the toolbox and field list are visible (recall
Figure 6.3).
8.2 Learning objectives
How do I create a bound combo box? 8.3.1 Creating a bound combo box
Can I create a combo box that displays values Although Access has a wizard that simplifies the pro-
from a different table? cess of creating combo boxes, you will start by build-
How do I show additional information in a
ing a simple combo box (similar to that shown in
Figure 8.1) with the wizard turned off. This will give
combo box?
you a better appreciation for what the wizard does
How do I prevent certain information from and provide you with the skills to make refinements
showing in the combo box? to wizard-created controls.
Can I change the order in which the items • Delete the existing Activity text box by select-
appear in a combo box? ing it and pressing the Delete key.
• The wizard toggle button ( ) in the toolbox an unbound combo box, the easiest thing to
allows you to turn wizard support on and off. do is to delete it and try again.
Ensure the button is out (wizards are turned off). FIGURE 8.3: An unbound combo box (not what
• Click on the combo box tool ( ). The cursor you want).
turns into a small combo box.
• With the combo box tool selected, drag the
Activity field from the field list to the desired
location on the form’s detail section, as shown in
Figure 8.2.
The process of selecting a tool from the toolbox, and
then using the tool to drag a field from the field list
ensures that the control you create (text box, combo
box, etc.) is bound to a field in the underlying table or
query.
Since the control
is unbound, no
If you forget to drag the field in from the field field name
list, you will create an unbound combo box, as shows and the
shown in Figure 8.3. If you accidently create label is generic.
depressed.
Ensure the wizard button is not
activate
Click on the combo box button to
the combo box tool.
have
Drag the Activity field on to the detail area. If you
done this correctly, the name of the underlying
field should show in the combo box and the label
should take the value of the field’s caption
8.3.2 Filling in the combo box properties • Change the Row Source Type property to Value
In this section, you will tell Access what you want to List as shown in Figure 8.4. This tells Access to
appear in the rows of new combo box. expect a list of values in its Row Source property.
• Switch to form view and test the combo box.
FIGURE 8.4: Set the Row Source Type property.
At this point, the combo box does not show any list
items because we have not specified what the list
items should be. There are three methods of specify-
ing what shows up in the combo box list:
1. enter a list of values into the combo box’s Row
Source property;
2. tell Access to get the value from an existing table
or query;
3. tell Access to use the names of fields in an exist-
ing table (you will not use this approach).
Although the second method is the most powerful
and flexible, you will start with the first.
• Bring up the property sheet for the Activity
combo box.
• Enter the following into the Row Source property: 8.3.3 A combo box based on another
LAB;LEC;TUT table or query
• Set the Limit To List property to Yes.
An obvious limitation of the value-list method of cre-
ating combo boxes is that it is impossible to change
If the Limit To List property is set to No, the
or update the items that appear in the list without
user can ignore the choices in the combo box
and simply type in a value (e.g., “SEM”). In knowing about the Row Source property.
this particular situation, you want to limit the A more elegant and flexible method of populating the
user to the three choices given. rows of a combo box is to have Access look up the
values from an existing table or query. Although the
• Switch to form view and experiment with the basic process of setting the combo box properties
combo box. remains the same, it is more efficient to rely on the
wizard when building this type of combo box.
Notice that the combo box has some useful
Before you can continue, you need a table that con-
built-in features. For example, if you choose
tains appropriate values for course activities.
to type values rather than select them with a
mouse, the combo box anticipates your • Switch to the database window and create a new
table called Activities.
choice based on the letters you type. Thus, to
select “TUT”, you need only type “T”. • The table should consist of two fields: one called
Activity and the other called Descript, as
shown in Figure 8.5.
FIGURE 8.6: Create a combo box using the combo box wizard.
combo
Create a bound
box.
values
Have Access look up the
from a table or query.
2. the field (or fields) that you would like to show up source table or query change while the form is
as columns in the in the combo box; open these changes are not automatically
3. the width of the field(s) in the combo box (see reflected in the combo box rows. As a conse-
Figure 8.7); quence, you have to either (a) close and re-
4. the column from the combo box (if more than one open the form, or (b) requery the form.
field is showing) that is inserted into the underly- Although you can automate the requery pro-
ing field; and, cess, we will rely on the F9 key for the time
5. the label attached to the field (see Figure 8.8). being.
When you are done, the combo box should look sim-
8.3.3.1 Showing more than one field in the
ilar to that shown in Figure 8.1. However, updating or
combo box
changing the values in the combo box is much easier
when the combo box is based on a table. One problem the combo boxes created so far is that
• Add “SEM” (Seminar) to the Activities table. they are not of much use to a user who is not familiar
• Return to the form, click on the Activity combo with the abbreviations “TUT”, “SEM”, and so on. In
box, and press F9 to requery the combo box. this section, you will use the Descript field of the
• Verify that “SEM” shows up in combo box. Activities table to make the combo box more
readable, as shown in Figure 8.9.
Access creates the rows in a combo box • Delete the existing combo box and start again.
when the form is opened. If the values in the
more
The combo box can show
than one field. Select
Activities
The new
only Activity for now.
tables contains
the values for
the combo box.
FIGURE 8.8: Fill in the combo box wizard dialog sheets (continued).
Activities
Bring both fields from the
table into the combo box.
resize
Uncheck the “hide key” box and
the columns appropriately.
Note that Access version 2.0 does
not have the “hide key” feature
the
Click on the right side of
column selector and
drag the edge of the
Activity column to the
far left (i.e., make its width
zero)
2. If the table does have a primary key, then the If the changes are quite minor (for instance, sorting
records are sorted in ascending order according the records in a different order), you may prefer to
to the key. modify the Row Source property.
It may be, however, that you want a different order In Section 8.3.2, you set the Row Source property to
within the rows of the combo box. To achieve this, equal a list of values. When the combo box is based
you can do one of two thing: on values from a table or a query, however, the Row
1. Create a stand-alone query (in which the sort Source is an SQL statement (recall Tutorial 5) rather
order is specified) and use this query as the than a list of values. You can either edit the SQL
source for the combo box. statement directly or invoke the QBE editor.
2. Modify the “ad hoc” query within the Row Source In this section, you will order the items in you combo
property of the combo box. box according to the length of the Descript field
If you intend to make several major changes to the (this is done merely for illustrative purposes).
basic information in the underlying table (e.g., joins, • Bring up the property sheet for the Activity
calculated fields), it is usually better to create a combo box.
stand-alone query. In this way, the same query can • Put the cursor in the Row Source property. As
be used by many combo boxes. shown in Figure 8.13, a builder button ( )
appears.
• Press the builder button to enter the “SQL
builder” (i.e., the QBE editor).
called
Add a calculated field trol, the new control becomes the last item in the tab
DescLength. order regardless of its position on the form.
To illustrate the problem, you are going to create a
combo box for the DeptCode field.
• Delete the DeptCode text box and replace it with
a combo box based on the Departments table.
• Switch to form view. Notice that the focus starts
off in the CrsNum field instead of the DeptCode
field.
calculated
Sort on the
field. Uncheck the
Show box • Press tab to move from field to field. Notice that
after DeptCode is left, the focus returns to the
CrsNum field of the next record.
• To fix the problem, return to form design mode For example, it never makes sense to put a combo
and select View > Tab Order from the main box on a non-concatenated primary key. To illustrate
menu. this, consider the Departments form shown in
Figure 8.16. On this form, the DeptCode text box
8.4 Discussion
8.4.1 Why you should never use a
combo box for a non-concatenated
key.
A mistake often made once new users learn how to This combo box appears to work. However, if you
make combo boxes is to put a combo box on every- think about it, it makes no sense: The form in
thing. There are certain situations, however, in which Figure 8.16 is a window on the Departments table.
the use of a combo box is simply incorrect. As such, when the DeptCode combo box is used,
selector
Drag the record
to the selector
Click on the record
of the field
desired position in you wish to move.
the list.
one of two things can occur depending on whether a combo box is identical to typing “CPSC” over
new record is being created or an existing record is whatever is currently in the DeptCode field. This
being edited: causes all sorts of problems; the most obvious of
1. A new record is being created — If a new these is that by overwriting an existing value of
record is being created (i.e., a new department is DeptCode, a “duplicate value in index, primary
being added to the information system), a unique key, or relationship” error is generated (there is
value of DeptCode must be created to distin- already a department with “CPSC” as its Dept-
guish the new department from the existing Code).
departments. However, the combo box only Note that a combo box may make sense when the
shows DeptCode values of existing depart- key is concatenated. An example of this is the
ments. If the Limit To List property is set to Yes, DeptCode combo box you created in Section 8.3.4.
then the combo box prevents the user from enter-
ing a valid DeptCode value. 8.4.2 Controls and widgets
2. An existing record is being edited — It is Predefined controls are becoming increasingly popu-
important to remember that a combo box has no lar in software development. Although Microsoft
intrinsic search capability. As such, selecting includes several predefined controls with Access
“CPSC” in the DeptCode combo box does not (such as combo boxes, check boxes, radio buttons,
result in a jump to the record with “CPSC” as its etc.), a large number of more compex or specialized
key value. Rather, selecting “CPSC” from the controls are available from Microsoft and other ven-
dors. In addition, you can write your own custom 8.5 Application to the assignment
controls using a language like Visual C++ or Visual
There are a number of forms in your assignment that
Basic and use them in many different forms and
can be greatly enhanced by combo boxes.
applications.
• Create a combo box on your order form to allow
An example of a more complex control is the calen- the user to select customers by name rather than
dar control shown in Figure 8.17. A calendar control CustID. Since your CustID value is a counter, it
can be added to a form to make the entry of dates has no significance beyond its use as a primary
easier for the user. Microsoft calls such components key. Generally, such keys should be hidden from
“ActiveX controls” (formerly known as “OLE con- view.
trols”). Non-microsoft vendors provide similar com- • Create a combo box in your order details subform
ponents but use different names, such as “widgets”. to allow the user to select products. Since the
There are two main advantages of using controls. ProductID values are used by both you and
First, they cut down on the time it takes to develop your customers, they have some significance
an application since the controls are predefined and beyond the information system. As such, Pro-
pre-tested. Second, they are standardized so that ductID should be visible in all combo boxes. In
users encounter the same basic behavior in all appli- addition, the items in the product list should be
cations. sorted by ProductID. This makes it easier to
select a product by typing the first few numbers.
• Create combo boxes on other forms as required.
half year]. Recall that you have already imple- • Test your form. Note that you are prevented from
mented this feature in Section 4.3.3.2 using a calcu- editing the calculated field. If, however, you
lated query field. change the value of Credits, the value of txt-
• Perform the steps shown in Figure 9.1 to create CourseLength changes accordingly when you
an unbound text box on your fmrCoursesMain leave the Credits field.
form.
• Set the Control Source property of the text box 9.3.2 Showing a total on the main form
using the syntax: In this section, you will create a calculated text box
= <expression> that displays the number of sections associated
In this case, the expression should be an “imme- with each course. The primary motivation for this
diate if” function (see Section 4.3.3.2). exercise is to illustrate some of the limitations of cal-
culated controls (as they are implemented in Access)
By default, Access interprets text in the Con-
and to provide an opportunity to explore an interest-
trol Source property field as the name of a
ing work-around.
variable (i.e., the name of a field or another
• Create a text box call txtNumSections on the
control). As such, you must remember to
main form as shown in Figure 9.2.
include the equals sign when setting this
property. The logical next step is to set the Control Source of
the field to an expression that includes the Count()
function. However, Access has a limitation in this
Credits
Make some room by dragging the
text box to the left. tool
Select the text box
from the
toolbox and click on
an appropriate space
in the detail area.
(e.g.,
Edit the label and give the text box a meaningful name
txtCourseLength). The txt prefix is used
here to indicate an unbound text box.
FIGURE 9.2: Create an unbound text box to show the number of sections
associated with each course.
Since
Add an unbound text box called txtNumSections.
it is currently bound to nothing, it is blank.
}
displaying the
count in the
new text box.
regard: you cannot use an aggregate function 9.3.2.1 Calculating the aggregate function on
(Sum(), Avg(), Count(), etc.) on a main form that the subform
refers to a field in a subform. As a consequence, you • Create an unbound text box on the subform as
have to break the calculation into two steps: shown in Figure 9.3.
1. use the aggregate function to create a calculated • Save the subform but do not close it.
text box on the subform (i.e., a “dummy” field to • Return to the main form and set the Control
hold an intermediate result); Source of txtNumSections to equal the value
2. create a calculated control on the main form that of txtNumSectionsOnSub. Since the naming
references the dummy text box created in the first conventions for objects on forms and subforms
step. can be tricky, use the expression builder (as
shown in Figure 9.4) to build the name for you.
It is important that you realize that this proce- The expression builder organizes all the elements of
dure does not involve any immutable, funda- the database environment into a hierarchical struc-
mental information systems knowledge. ture. You build an expression by “drilling down” to the
Rather, it is merely an example of the type of element you need and double-clicking to copy its
work-around (hack, kludge, etc.) that is rou- name into the text area.
tinely used when using a tool like Access to
create a custom application. The expression builder takes some practice.
One problem is that it is easy to double-click
txtNumSectionsOnSub
Create a calculate control called
and place it in the form header
(do not worry about its location, you will move it later).
property
Set the Control Source
to
=Count([Section]).
Note that any field can be
used as the argument for the
Count() function.
FIGURE 9.4: Use the builder to drill down to the calculated control on the subform.
Control
Invoke the builder from the
Source property and drill
down to the calculated control you
just created on the subform.
Note that when the main
form and the subform are
both open, the subform
appears twice in the builder:
once as a “stand-alone”
form (under “Loaded
Forms”) and once as a
component of the main form
(press the + sign on the
frmCoursesMain
folder). You want to use the
latter (you will never
access the subform in stand-
alone mode).
on the wrong thing. Another problem is that • Test the form. The value of txtNumSections
Access attempts to guide you by inserting and txtNumSectionsOnSub should be identi-
«Expr» place-holders all over the place. The cal, as shown in Figure 9.5.
solution to both problems is to click on the text FIGURE 9.5: The number of sections on the main
window and make liberal use of the Delete form.
key.
9.3.2.2 Hiding the text box on the subform • Drag (or cut and paste) txtNumSectionsOn-
The obvious problem in Figure 9.5 is that the dummy Sub from the form header to the page header, as
text box shows on the subform. There are at least shown in Figure 9.6.
two ways to get around this: one is to set the Visible • Test the result.
property of the text box to No; a slightly more elegant
approach is to use the page header or page footer 9.4 Discussion
to hide the text box. In Section 4.3.3.2 and Section 9.3.1, you accom-
The page header and footer are areas on the form plished the same thing (showing half year or
that only show when the form is printed. Since you full year) using different techniques. The advan-
will never print a form (reports are used for printed tage of implementing this as a calculated query field
material), these areas can be used to hide intermedi- is that you can use this field repeatedly in other
ate results, etc. forms. On the other hand, if you do the transforma-
• In design mode, select View > Page Header/ tion on the form, you have to repeat the calculation
Footer from the menu. on every form that requires the calculated field.
In the case of the aggregate function, the situation is
menu
Select View > Page Header/Footer from the
(Format > Page Header/Footer in version the
Drag (or cut and paste)
field you want to hide
2.0) to show the page header and footer. into the page header.
(here
Run the query and supply a parameter value
Access is asking for the value of X).
(“COMM”)
Replace the literal criterion
with a parameter (X)
Note that the spelling mistakes discussed in may be useful to be able to generate a list of courses
Section 4.3.4 are processed by Access as offered by the department currently being viewed.
parameters. Although you could use a creatively-named parame-
ter to invoke the “Enter Parameter Value” dialog, this
10.3.2 Using parameters to generate requires the user to type in the value of DeptCode.
prompts A more elegant approach is to have Access pull the
Since the name of the parameter can be anything value of a parameter directly from the open form.
(as long as it is enclosed in square brackets), you This exploits the second step in the operation of a
can exploit this feature to create quick and easy dia- parameter query (Access will attempt to resolve a
log boxes. parameter with the value of an object within the cur-
• Change the name of your DeptCode parameter rent environment). The basic idea is shown in
from [X] to [Courses for which depart- Figure 10.3.
ment?]. The key to making this work is to provide a parame-
• Run the query, as shown in Figure 10.2. ter name that correctly references the form object in
which you are interested. In order to avoid having to
10.3.3 Values on forms as parameters remember the complex naming syntax for objects on
A common requirement is to use the value on a form forms, you can invoke the expression builder to
to influence the outcome of a query. For instance, if select the correct name from the hierarchy of data-
the user is viewing information about departments, it base objects.
the
When Access asks for
value of the
parameter, it uses the
parameter’s name.
for
Name the parameter [Courses
which department?].
• Create a very simple form based on the Although the naming syntax of objects in
Departments table and save it as frmDepart- Access is tricky, it is not impossible to com-
ments. prehend. For example, the name
• Leave the form open (in form view or design Forms![frmDepartments]![DeptCode]
mode, it does not matter). consists of the following elements: Forms
• Open pqryCourses in design mode, place the refers to a collection of Form objects; [frm-
cursor in the criteria row of the DeptCode field, Departments] is a specific instance of a
and invoke the expression builder as shown in Form object in the Forms collection; [Dept-
Figure 10.4. Code] is a Control belonging to the form. See
• Perform the steps shown in Figure 10.5 to create Tutorial 14 for more information on the hierar-
a parameter that references the DeptCode field chy of objects used by Access.
on the frmDepartments form.
• Run the query. The results set should correspond 10.4 Application to the assignment
to the department showing in the frmDepart- You will use parameter queries as the basis for sev-
ments form. eral action queries (see Tutorial 11) that process
• Move to a new record on the form. Notice that transactions against master tables. For now, simply
you have to requery the form (Shift-F9) in order create the parameter queries that take their criteria
for the new parameter value to be used (see values from forms you have already created.
Figure 10.6).
Criteria
Place the cursor in the
row of the
DeptCode field and
right-click to bring up
the pop-up menu.
Departments
Create a simple form based on the
table and leave it open
in the background.
Select Build to
invoke the builder.
FIGURE 10.5: Use the builder to select the name of the object you want to use as a parameter.
the
Select Forms to get a list of all
forms in your database.
form
Since the frmDepartments
is open, click on Loaded
Forms and select the form.
Press OK
when done.
Move to the middle pane and
select Field List to get a list of the
The text will
be copied
fields on the form in the pane on into the
the far right. criteria row.
FIGURE 10.6: Requery the results set to reflect changes on the form.
form.
Move to a new record on the
Notice that the query is not
automatically updated.
parameter
Press Shift-F9 to requery. The new
value (MATH in this case)
is used to select records.
• Create a parameter query to show all the order • It shows the change (positive or negative but not
details for a particular order. zero) in backorders for each item in a particular
• Create a second parameter query to show all the order.
shipment details for a particular shipment. • The query consist of three fields: OrderID, Pro-
Each order may result in a number of changes being ductID and a calculated field Qty (i.e., the
made to the BackOrders table. For some items in change in the back order for a particular product).
the order, more product is ordered than is actually • The name of the parameter is in this query is sim-
shipped (i.e., a backorder is created). For other ply[pOrderID]. Since the value of this parame-
items, more product is shipped than is ordered (i.e., ter will be set by the Visual Basic shortcut before
a backorder is filled). the query is run, there is no need to set it to a
value on a form.
In Tutorial 15, you are supplied with a “shortcut”
Visual Basic procedure that makes the changes to Since the query is accessed by a program,
the BackOrders table for you. However, the short- the name of the query and all the fields must
cut procedure requires a query that lists the changes be exactly as described above. In other
that must be made to the BackOrders table for a words, you are given a precise specification
particular order. The requirements for this query are for a database object that fills a role in a pro-
the following: cess designed and implemented by someone
• The name of the query is else. You will not understand how the query
pqryItemsToBackOrder fits in until Tutorial 15.
ues. There are at least four different ways of accom- 3. Write a Visual Basic program to automate Step 2.
plishing this task: This is a good approach; however, it clearly
1. Create a calculated field called NewCredits that requires the ability to write Visual Basic pro-
multiplies the value of Credits by 1.5 — The grams.
query containing the calculated field can be used 4. Create an update query that (a) selects only
in place of the Courses table whenever credit those courses that require modification and (b)
information is required. Of course, the values replaces the value of Credits with Credits *
stored in the Courses table are still the old val- 1.5. — This approach is computationally efficient
ues. Although there might be some advantages and allows you to work with the QBE editor rather
to keeping the old values, it may cause confusion than a programming language.
about which values to use. In addition, the use of
a calculated field creates a computational load 11.2 Learning objectives
that becomes larger as the number of courses What is an action query? Why would I want to
increases. use one?
2. Go through the Courses table record by record
and manually change all the values — This
How do I make a backup copy of one of my
tables?
approach is tedious and error prone. Further-
more, it is simply impractical if the number of How to I undo (rollback) an action query once
courses is large. I have executed it?
How do I update only certain records in a • While still in query design mode, select Query >
table? Make Table from the main menu and provide a
How do I create a button on a form? How do I name for the target table (e.g., CoursesBackup)
make an action query execute when the as shown in Figure 11.1.
button is pressed? • Select Query > Run from the main menu to exe-
cute the action query, as shown in Figure 11.2.
11.3 Tutorial exercises Action queries do not execute until you explic-
itly run them. Switching to datasheet mode
11.3.1 Using a make-table query to create only provides a preview of the results set.
a backup
Since action queries permanently modify the data in • Save the query. If you switch to the database win-
tables, it is a good idea to create a backup of the dow, you will notice that the new make-table
table in question before running the query. An easy query has a different icon than the select queries.
way to do this is to use a make-table query.
• Create a select query based on the Courses 11.3.2 Using an update query to rollback
table and save it as qryCoursesBackup. changes
• Project the asterisk (*) into the query definition so Having a backup table is not much use without a
that all the fields are included in the results set. means of using it to restore the data in your original
table. In this section, you will use an update query to
the
Project all fields (*) into
query definition.
Query
To execute the query, you must select
> Run. Alternatively, you can
press the “run” (!) icon on the toolbar.
tion:
FIGURE 11.4: Fill in the Update To field.
Courses.DeptCode =
CoursesBackup.DeptCode AND
the
Select Query > Update to make
query an update query.
Courses.CrsNum =
CoursesBackup.CrsNum.
2. By projecting Courses.Credits into the query,
you are making it the target for the update. In
other words, the values in Courses.Credits
are going to be modified by the update action.
3. By setting the Update To field to Courses-
Backup.Credits, you are telling Access to
replace the contents of Courses.Credits with
the contents of CoursesBackup.Credits.
Whenever this query is run, it will replace whatever is
in the Credits field of all the records in the
Courses table with values from the backup. You will
use this query to “rollback” updates made later on.
syntax
Use the <table name>.<field name>
to disambiguate the field name.
• Run the query and verify that update has been 11.3.5 Attaching action queries to
performed successfully. buttons
As a designer, you should not expect your users to
11.3.4 Rolling back the changes understand your query naming convention, rum-
While testing the qryUpdateCredits query, your mage through the queries listed in the database win-
exuberance may have led you to execute it more dow, and execute the queries that need to be
than once. To return the Courses table to its state executed. As such, it is often useful to create buttons
before any updates, all you need to do it run your on forms and “attach” the action queries to the but-
rollback query. tons. When the button is pressed, the query is exe-
• Run qryRollback credits by double-clicking its cuted.
icon in the database window.
Although we have not yet discussed buttons (or
Once an action query is created, it has more events in general), the button wizard makes the cre-
in common with subroutines written in Visual ation of this type of form object straightforward.
Basic than standard select queries. As such, it • Modify qryUpdateCredits so that it updates
is best to think of action queries in terms of only those departments matching the DeptCode
procedures to be executed rather than virtual value in the frmDepartments table (see
tables or views. Double-clicking an action Figure 11.6).
query executes it. • Save the resulting action parameter query as
pqryUpdateCredits and close it.
FIGURE 11.6: Create an action parameter query to update Credits for a particular department.
specifies
The update operation
the action to update
The criterion limits the scope of the
to those records matching
perform on the records. the current parameter value
• Switch to the design view of frmDepartments • Create backup copies of your Products and
and add a button as shown in Figure 11.7. BackOrders tables using make-tables queries.
• Attach the pqryUpdateCredits query to the Save these queries but note that they only need
button as shown in Figure 11.8. to be run once.
• Provide a caption and a name for the button as • Create a rollback query that allows you to return
shown in Figure 11.9. your Products table to its original state.
• Switch to form view. Press the button to run the Rolling back the BackOrders table is more complex
query (alternatively, use the shortcut key by than rolling back the Products table. This is
pressing Alt-U) as shown in Figure 11.10. because we are making the assumption that no
products are ever added or deleted to the system. As
11.4 Application to the assignment such, all the information needed for the rollback is in
the backup copy of Products.
11.4.1 Rolling back your master tables
In contrast, records are added to the BackOrders
As you begin to implement the transaction process- table on a regular basis. As a result, the Back-
ing component of your system, it is worthwhile to Orders table and its backup may contain a different
have a means of returning your master tables to their number of records. If so, the match-and-replace pro-
original state (i.e., their state when you started devel- cess used for rolling back Products is inappropri-
oping the system). ate.
FIGURE 11.7: Add a button to the form using the button wizard.
the
If there is insufficient space for a button, drag
border of the detail section to the right
wizard
Ensure that the
button in the
toolbox is
depressed (wizards
are activated).
FIGURE 11.8: Use the wizard to attach an action query to the button.
perform
Buttons can be created to
many different actions
in Access. The button wizard
organizes these actions into
categories. Select
Miscellaneous > Run Query.
queries
The wizard lists all the available
(including non-action queries).
Select pqryUpdateCredits.
button.
Provide a meaningful name for the
The cmd prefix indicates a
command button.
(or
Press the button to execute the action query
press Alt-U to use the shortcut).
The easiest way to rollback the BackOrders table is ries. These queries will allow you to perform
to delete all the records it contains and use an reasonably complex transaction processing opera-
append query to replace the records from the tions on your master tables.
backup. • Create an update query to add all products in a
• Open your BackOrders table in datasheet mode shipment to inventory.
and select Edit > Select All Records from the
menu (alternatively, press Control-A) Note that this query should only process ship-
• Press the Delete key. ment details for the current shipment (i.e., it
• Create an append query that adds the records should be based on a parameter query similar
in the backup table to the BackOrders table. to the one you created in Section 10.4).
Once you learn the Access macro language or • Create a button on the shipments form to perform
Visual Basic for Applications, you will be able to write this update.
a small procedure to execute these steps for you. • Create an update query to subtract items from
For the assignment, however, this “manual rollback” inventory when you process an order from your
is sufficient. customers. Do not attach this query to a button at
this point.
11.4.2 Processing transactions
You are now in a position to combine parameter que- This query should only process order details
ries and action queries into parameter-action que- from the current order.
Programming can be an enormously complex and 12.1.1 Interacting with the interpreter
difficult activity. Or it can be quite straightforward. In
Access provides two ways of interacting with the
either case, the basic programming concepts remain
VBA language. The most useful of these is through
the same. This tutorial is an introduction to a handful
saved modules that contain VBA procedures. These
of programming constructs that apply to any “third
procedures (subroutines and functions) can be run to
generation” language, not only Visual Basic for
do interesting things like process transactions
Applications (VBA).
against master tables, provide sophisticated error
Strictly speaking, the language that is checking, and so on.
included with Access is not Visual Basic—it is The second way to interact with VBA is directly
a subset of the full, stand-alone Visual Basic through the interpreter. Interpreted languages are
language (which Microsoft sells separately). easier to experiment with since you can invoke the
In Access version 2.0, the subset is called interpreter at any time, type in a command, and
“Access Basic”. In version 7.0, it is slightly watch it execute. In the first part of this tutorial, you
enlarged subset called “Visual Basic for Appli- are going to invoke Access’ VBA interpreter and exe-
cations” (VBA). However, in the context of the cute some very simple statements.
In the second part of the tutorial, you are going to 12.3 Tutorial exercises
create a couple of VBA modules to explore looping,
conditional branching, and parameter passing. 12.3.1 Invoking the interpreter
• Click on the module tab in the database window
12.2 Learning objectives and press New.
What is the debug/immediate window? How This opens the module window which we will use in
do I invoke it? Section 12.3.3. You have to have a module window
What are statements, variables, the open in order for the debug window to be available
assignment operator, and predefined from the menu.
functions? • Select View > Debug Window from the main
How do I create a module containing VBA menu. Note that Control-G can be used in ver-
sion 7.0 and above as a shortcut to bring up the
code?
debug window.
What are looping and conditional branching?
What language constructs can I use to In version 2.0, the “debug” window is called
implement them? the “immediate” window. As such, you have to
How do I use the debugger in Access? use View > Immediate Window. The term
When the second statement is executed, VBA recog- is a function that is provided as part of the program-
nizes that s is a variable, not a string (since it is not ming environment.
in quotations marks). The interpreter replaces s with For example, cos(x) is a predefined function in
its value (Hello) before executing the Print com- many computer languages—it takes some number x
mand. In the final statement, s is in quotation marks as an argument, does some processing to find its
so it is interpreted as a literal string. cosine, and returns the answer. Note that since this
function is predefined, you do not have to know any-
Within the debug window, any string of char-
thing about the algorithm used to find the cosine, you
acters in quotations marks (e.g., “COMM”) is
just have to know the following:
interpreted as a literal string. Any string with-
out quotation marks (e.g., COMM) is interpreted 1. what to supply as inputs (e.g., a valid numeric
as a variable (or a field name, if appropriate). expression representing an angle in radians),
Note, however, that this convention is not uni- 2. what to expect as output (e.g., a real number
versally true within different parts of Access. between -1.0 and 1.0).
12.3.2.3 Predefined functions The on-line help system provides these two
pieces of information (plus a usage example
In computer programming, a function is a small pro-
and some additional remarks) for all VBA pre-
gram that takes one or more arguments (or param-
defined functions.
eters) as input, does some processing, and returns
a value as output. A predefined (or built-in) function
A module contains a declaration page and one or 12.3.4 Creating subroutines with looping
more pages containing subroutines or user-defined and branching
functions. The primary difference between subrou- In this section, you will explore two of the most pow-
tines and functions is that subroutines simply exe-
erful constructs in computer programming: looping
cute whereas functions are expected to return a and conditional branching.
value (e.g., cos()). Since only one subroutine or
• Create a new subroutine by typing the following
function shows in the window at a time, you must anywhere on the declarations page:
use the Page Up and Page Down keys to navigate
Sub LoopingTest()↵
the module.
Notice that Access creates a new page in the mod-
The VBA editor in version 8.0 has a number of ule for the subroutine, as shown in Figure 12.3.
enhancements over earlier version, including 12.3.4.1 Declaring variables
the capability of showing multiple functions
When you declare a variable, you tell the program-
and subroutines on the same page.
ming environment to reserve some space in memory
for the variable. Since the amount of space that is
required is completely dependent on the type of data
the variable is going to contain (e.g., string, integer,
Boolean, double-precision floating-point, etc.), you
• Copy the highlighted code to the clipboard (Con- 12.3.7 Creating the Min() function
trol-Insert), switch to ParameterTest, and In this section, you are going to create a user-
paste the code (Shift-Insert) into the Parame- defined function that returns the minimum of two
terTest procedure. numbers. Although most languages supply such a
To incorporate the parameters into ParameterT- function, Access does not (the Min() and Max()
est, you will have to make the following modifica- function in Access are for use within SQL statements
tions to the pasted code: only).
• Replace i = 1 with i = intStart. • Create a new module called basUtilities.
• Replace i > 10 with i > intStop. • Type the following to create a new function:
• Call the subroutine from the debug window by Function MinValue(n1 as Single, n2
typing: as Single) as Single↵
ParameterTest 4, 12↵ This defines a function called MinValue that returns
a single-precision number. The function requires two
If you prefer enclosing parameters in brack-
single-precision numbers as parameters.
ets, you have to use the Call <sub
name>(parameter1, ..., parametern) Since a function returns a value, the data type
syntax. For example: of the return value should be specified in the
Call ParameterTest(4,12)↵ function declaration. As such, the basic syn-
tax of a function declaration is:
function
Implement the MinValue()
using conditional branching.
various
Test the function by passing it
parameter values.
13.1.1 Triggers
FIGURE 13.1: In a trigger, a procedure is
attached to an event. Since events on forms “trigger” actions, event/proce-
dure combinations are sometimes called triggers.
An object, such as the
interface object button created in For example, the action query you attached to a but-
cmdUpdateCredits Section 11.3.5, has ton in Section 11.3.5 is an example of a simple, one-
predefined properties and action trigger. However, since an action query can
properties events. For a button, the
Caption most important event is only perform one type of action, and since you typi-
Enabled On Click. cally have a number of actions that need to be per-
... formed, macros or Visual Basic procedures are
events procedure typically used to implement a triggers in Access.
On Click
On Got Focus
... 13.1.2 The Access macro language
As you discovered in Tutorial 12, writing simple VBA
A procedure (such as an programs is not difficult, but it is tedious and error-
action query, macro, or VBA prone. Furthermore, as you will see in Tutorial 14,
function or subroutine) can be VBA programming becomes much more difficult
attached to an event. When
the event occurs, the when you have to refer to objects using the naming
procedure is executed. conventions of the database object hierarchy. As a
consequence, even experienced Access program-
mers often turn to the Access macro language to attach the procedure to the correct event of the cor-
implement basic triggers. rect object.
The macro language itself consists of 40 or so com-
Selecting the correct object and the correct
mands. Although it is essentially a procedural lan-
event for a trigger is often the most difficult
guage (like VBA), the commands are relatively high
part of creating an event-driven application. It
level and easy to understand. In addition, the macro
is best to think about this carefully before you
editor simplifies the specification of the action argu-
get too caught up in implementing the proce-
ments (parameters).
dure.
13.1.3 The trigger design cycle
13.2 Learning objectives
To create a trigger, you need to answer two ques-
tions: What is event-driven programming? What is a
trigger?
1. What has to happen?
2. When should it happen? How do I design a trigger?
Once you have answered the first question (“what”), How does the macro editor in Access work?
you can create a macro (or VBA procedure) to exe- How do I attach a macro to an event?
cute the necessary steps. Once you know the
answer to the second question (“when”), you can
What is the SetValue action? How is it used?
How do I make the execution of particular 13.3.1 The basics of the macro editor
macro actions conditional? In this section, you are going to eliminate the warn-
What is a switchboard and how do I create ing messages that precede the trigger you created
one for my application? Section 11.3.5.
How to I make things happen when the As such, the answer to the “what” question is the fol-
application is opened? lowing:
What are the advantages and disadvantages 1. Turn off the warnings so the dialog boxes do not
of event-driven programming? pop up when the action query is executed;
2. Run the action query; and,
13.3 Tutorial exercises 3. Turn the warnings back on (it is generally good
programming practice to return the environment
In this tutorial, you will build a number of very simple
to its original state).
triggers using Access macros. These triggers, by
themselves, are not particularly useful and are Since a number of things have to happen, you can-
intended for illustrative purposes only. not rely on an action query by itself. You can, how-
ever, execute a macro that executes several actions
including one or more action queries.
the
Add the three commands to
macro.
SetWarnings
The arguments for the two
actions
are straightforward. For the
OpenQuery command,
you can select the query to
open (or run) from a list.
Since this is an action
query, the second and third
arguments are not
applicable.
ofPress
functions and subroutines) is embedded in the arrow to get a list
the frmDepartments form. available macros
• Switch to form view and press the button. Since to modify the structure of the table until the
no warnings appear, you may want to press the query or form is closed.
button a few times (you can always use your roll-
back query to reset the credits to their original • Set the Caption property to Credits updated?
values). and the Default property to No as shown in
Figure 13.6.
13.3.3 Creating a check box to display Changes made to a table do not automatically carry
update status information over to forms already based on that table. As such,
Since the warning boxes have been disabled for the you must manually add the new field to the depart-
update credits trigger, it may be useful to keep track ments form.
of whether courses in a particular department have • Open frmDepartments in design mode.
already been updated. • Make sure the toolbox and field list are visible.
Notice that the new field (CrUpdated) shows up
To do this, you can add a field to the Departments
in the field list.
table to store this “update status” information.
• Use the same technique for creating combo
• Edit the Departments table and add a Yes/No
boxes to create a bound check box control for the
field called CrUpdated.
yes/no field. This is shown in Figure 13.7.
If you have an open query or form based on
the Departments table, you will not be able
FIGURE 13.7: Add a check box control to keep track of the update status.
from
Select the check box tool
the toolbox.
FIGURE 13.8: Add a SetValue command to set the value of the update status field when the
update is compete.
from
Pick the SetValue command
the list or simply type it in.
The Item argument is the thing you The Expression argument is the
value you want the SetValue
want the SetValue action to set the action to set the value of the Item
value of. You can use the builder or to. Type in Yes (no quotation
simply type in CrUpdate. marks are required since Yes is
recognized as a constant in this
context).
• Select View > Conditions to display the condi- 13.3.5.1 The simplest conditional macro
tions column in the macro editor as shown in If there is an expression in the condition column of a
Figure 13.9. macro, the action in that row will execute if the condi-
FIGURE 13.9: Display the macro editors tion is true. If the condition is not true, the action will
condition column be skipped.
• Fill in the condition column as shown in
Figure 13.10. Precede the actions you want to
execute if the check box is checked with [CrUp-
dated]. Precede the actions you do not want to
execute with Not [CrUpdated].
“conditions”
Select View > Conditions or press the true and false parts are implied. However, if a
button on the tool bar. non-Boolean data type is used in the expres-
sion, a comparison operator must be included
(e.g., [DeptCode] = “COMM”, [Cred-
its] < 3, etc.)
true
The expression [CrUpdated] is
if the CrUpdated check box is
checked. In this situation, you should
indicate to the user that the update is
not being performed.
• Switch to the form and test the macro by pressing 13.3.5.2 Refining the conditions
the button. If the CrUpdated check box is The macro shown in Figure 13.10 can be improved
checked, you should get a message similar to by using an ellipsis (…) instead of repeating the
that shown in Figure 13.11. same condition in line after line. In this section, you
FIGURE 13.11: The action query is not executed will simplify your conditional macro slightly.
and the message box appears instead. Move the message box action and condition to the
top of the list of actions by dragging its record selec-
tor (grey box on the left).
• Insert a new row immediately following the mes-
sage and add a StopMacro action, as shown in
Figure 13.12.
The macro in Figure 13.12 executes as follows: If
CrUpdate is true (i.e., the box is checked), the
MsgBox action executes. Since the next line has an
ellipsis in the condition column, the condition contin-
ues to apply. However, that action on the ellipsis line
is StopMacro, and thus the macro ends without
executing the next four lines.
message
Click the record selector and drag the
box action to the top of the list.
13.3.5.3 Creating a group of named macros
It is possible to store a number of related macros
• Perform the steps in Figure 13.13 to modularize • Remove the scroll bars, navigation buttons, and
your macro. record selectors from the form using the form’s
• Change the macro referred to in the On Click property sheet.
property of the cmdUpdateCredits button from • Save the form as swbMain.
mcrUpdateCredits to There are two ways to add button-based triggers to a
mcrUpdateCredits.CheckStatus. form:
• Test the operation of the button.
1. Turn the button wizard off, create the button, and
attach an macro containing the appropriate
13.3.6 Creating switchboards
action (or actions).
One of the simplest (but most useful) triggers is an 2. Turn the button wizard on and use the wizard to
OpenForm command attached to a button on a form select from a list of common actions (the wizard
consisting exclusively of buttons. writes a VBA procedure for you).
This type of “switchboard” (as shown in
Figure 13.14) can provide the user with a means of Since the wizard can only attach one action to
navigating the application. a button (such as opening a form or running
• Create an unbound form as shown in an action query) it is less flexible than a
Figure 13.15. macro. However, once you are more comfort-
able with VBA, there is nothing to stop you
the
Select View > Macro Names to display
macro names column.
A macro executes until it encounters a
blank line. Use blank lines to separate the
named macros within a group.
CheckStatus
Create a named macro called
that contains the
conditional logic for the procedure.
particular
The RunMacro action executes a
macro. Select the macro to
execute from a list in the arguments pane.
Note the naming convention for macros
within a macro group.
The command buttons are placed on an Although it is not shown here, switchboards can
unbound form. Note the absence of scroll bars, call other switchboards, allowing you to add a
record selectors, or navigation buttons. hierarchical structure to your application.
Gratuitous clip art can be used to Shortcut keys are include on each
clutter your forms and reduce the button to allow the user to navigate
application’s overall performance. the application with keystrokes.
leave
Select Design View (no wizard) and
the “record source” box empty.
you
The result is a blank form on which
can build your switchboard.
from editing the VBA modules created by the • Follow the directions provided by the wizard to
wizard to add additional functionality. set the action for the button (i.e., open the frm-
Courses form) as shown in Figure 13.17.
13.3.6.1 Using a macro and manually-created • Change the button’s font and resize it as
buttons required.
• Ensure the wizard is turned off and use the but-
ton tool to create a button. You can standardize the size of your form
• Modify the properties of the button as shown in objects by selecting more than one and using
Figure 13.16. Format > Size > to Tallest and to Widest com-
• Create a macro called mands. Similarly, you can select more than
mcrSwitchboard.OpenDept and use the one object and use the “multiple selection”
OpenForm command to open the form frmDe- property sheet to set the properties all at
partments. once.
• Attach the macro to the On Click event of the
cmdDepartments button. 13.3.7 Using an autoexec macro
• Test the button. If you use the name autoexec to save a macro (in
13.3.6.2 Using the button wizard lieu of the normal mcr<name> convention), Access
• Turn the button wizard back on and create a new will execute the macro actions when the database is
button. opened. Consequently, auto-execute macros are
(ensure
Use the button tool to create a button
the wizard activated). (e.g.,
Give the button a meaningful name
cmdDepartments) and caption
(including a shortcut key.).
FIGURE 13.17: Use the command button wizard to create a button for the switchboard.
the
Select Form Operations > Open Form as
action type associated with the button.
Provide a caption
for the button.
from
Select the correct form
the list.
often used to display a switchboard when the user from the menu system. Consequently, you need to
starts the application. know something about the menu structure of Access
Another typical auto-execute operation is to hide the before you create your macro.
database window. By doing this, you unclutter the
screen and reduce the risk of a user accidentally
making a change to the application (by deleting a
In version 8.0, the DoMenuItem action has
been replaced by the slightly more intuitive
RunCommand action. See on-line help for
database object, etc.).
more information on RunCommand.
To unhide the database window, select Win-
• Create an auto-execute macro
dow > Unhide from the main menu or press
• Add the DoMenuItem and OpenForm actions to
the database window icon ( ) on the toolbar.
hide the database window and open the main
The problem with hiding the database window using switchboard, as shown in Figure 13.18.
a macro is that there is no HideDatabaseWindow • Close the database and reopen it after a short
command in the Access macro language. As such, delay to test the macro.
you have to rely on the rather convoluted DoMenu-
In version 7.0 and above, you do not need to
Item action.
use an autoexec macro to hide the database
As its name suggests, the DoMenuItem action per- window and open a form. Instead, you can
forms an operation just as if it had been selected right-click on the database window, select
13.4 Discussion
FIGURE 13.18: Create an auto-execute macro.
13.4.1 Event-driven programming versus
conventional programming
The primary advantages of event-driven program-
ming are the following:
1. Flexibility — since the flow of the application is
controlled by events rather than a sequential pro-
gram, the user does not have to conform to the
programmer’s understanding of how tasks should
be executed.
Window
For the DoMenuItem action, select the
> Hide commands from the
2. Robustness — Event-driven applications tend to
be more robust since they are less sensitive to
Database menu (i.e., the menu that is active
when the database window is being used). the order in which users perform activities. In
conventional programming, the programmer has
to anticipate virtually every sequence of activities
Startup, and fill in the properties for the appli- the user might perform and define responses to
cation. these sequences.
The primary disadvantage of event-driven programs • Create a main switchboard for you application. It
is that it is often difficult to find the source of errors should provide links to all the database objects
when they do occur. This problem arises from the your user is expected to have access to (i.e., your
object-oriented nature of event-driven applications— forms).
since events are associated with a particular object
you may have to examine a large number of objects
before you discover the misbehaving procedure.
This is especially true when events cascade (i.e., an
event for one object triggers an event for a different
object, and so on).
Unfortunately, the DAO hierarchy is somewhat more every object has a number of properties that can be
complex than this. However, at this level, it is suffi- either observed (read-only properties) or set (read/
cient to recognize three things about DAO: write properties). For example, each TableDef (table
1. Each object that you create is an instance of a definition) object has a read-only property called
class of similar objects (e.g., univ0_vx is a par- DateCreated and a read/write property called Name.
ticular instance of the class of Database objects). To access an object’s properties in VBA, you nor-
2. Each object may contain one or more Collec- mally use the <object name>.<property
tions of objects. Collections simply keep all name> syntax, e.g.,
objects of a similar type or function under one Employees.DateCreated.
umbrella. For example, Field objects such as
To avoid confusion between a property called
DeptCode and CrsNum are accessible through a
DateCreated and a field (defined by you)
Collection called Fields).
called DateCreated, Access version 7.0
3. Objects have properties and methods (see
and above require that you use a bang (!)
below).
instead of a period to indicate a field name or
some other object created by you as a devel-
14.1.2 Properties and methods
oper. For example:
You should already be familiar with the concept of Employees!DateCreated.Value
object properties from the tutorial on form design identifies the Value property of the DateCre-
(Tutorial 6). The idea is much the same in DAO:
ated field (assuming one exists) in the object summaries in the on-line help if you are
Employees table. unsure.
Methods are actions or behaviors that can be A more obvious example of a method is the Cre-
applied to objects of a particular class. In a sense, ateField method of TableDef objects, e.g.:
they are like predefined functions that only work in Employees.CreateField(“Phone”,
the context of one type of object. For example, all dbText, 25)
Field objects have a method called FieldSize that This creates a field called Phone, of type dbText (a
returns the size of the field. To invoke a object’s constant used to represent text), with a length of 25
methods, you use the characters.
<object name>.<method> [parameter1,
..., parametern] syntax, e.g.,: 14.1.3 Engines, workspaces, etc.
DeptCode.FieldSize. A confusing aspect of the DAO hierarchy is that you
cannot simply refer to objects and their properties as
A reasonable question at this point might be:
done in the examples above. As Figure 14.1 illus-
Isn’t FieldSize a property of a field, not a
trates, you must include the entire path through the
method? The answer to this is that the imple-
hierarchy in order to avoid any ambiguity between,
mentation of DAO is somewhat inconsistent in
say, the DeptCode field in the Courses TableDef
this respect. The best policy is to look at the
object and the DeptCode field in the qryCourses
QueryDef object.
Fields Fields
Legend
DeptCode DeptCode
TableDefs object or collection
Indexes Indexes
Courses instance
(dbCurr)
Declare and set the pointer
to the current
database.
ofAddthea database.
line to print the name
(in contrast to simple data types like integer, Do not worry if you are not completely sure
string, etc.) Access does not allocate memory what is going on at this point. As long as you
space for a whole database object. Instead, it understand that you can type the above two
allocates space for a pointer to a database lines to create a pointer to your database,
object. Once the pointer is created, you must set then you are in good shape.
it to point to an object of the declared type (the
object may exist already or you may have to cre- 3. Debug.Print dbCurr.Name
ate it). This statement prints the name of the object to
2. Set dbCurr = DBEngine.Work- which dbCurr refers.
spaces(0).Databases(0)
(Note: this should be typed on one line). In this 14.3.2 Creating a Recordset object
statement, the variable dbCurr (a pointer to a As its name implies, a TableDef object does not con-
Database object) is set to point to the first Data- tain any data; instead, it merely defines the structure
base in the first Workspace of the only Database of a table. When you view a table in design mode,
Engine. Since the numbering of objects within a you are seeing the elements of the TableDef object.
collection starts at zero, Databases(0) indi- When you view a table in datasheet mode, in con-
cates the first Database object. Note that the first trast, you are seeing the contents of Recordset
Database object in the Databases collection is object associated with the table.
always the currently open one.
To access the data in a table using VBA, you have to 2. Sets rsCourses to point to the newly created
invoke the OpenRecordset method of the Data- recordset.
base object. Since most of the processing you do in Note that this Set statement is different than the pre-
VBA involves data access, familiarity with Recordset vious one since the OpenRecordset method
objects is essential. In this section, you will create a results in a new object being created (dbCurr points
Recordset object based on the Courses table. to an existing database—the one you opened when
• Delete the Debug.Print dbCurr.Name line you started Access).
from your program.
• Add the following: 14.3.3 Using a Recordset object
Dim rsCourses As Recordset In this section, you will use some of the properties
Set rsCourses = and methods of a Recordset object to print its con-
dbCurr.OpenRecordset(“Courses”)
tents.
The first line declares a pointer (rsCourses) to a • Add the following to PrintRecords:
Recordset object. The second line does two things:
Do Until rsCourses.EOF
1. Invokes the OpenRecordset method of dbCurr Debug.Print rsCourses!DeptCode & “ ”
to create a Recordset object based on the table & rsCourses!CrsNum
named “Courses”. (i.e., the name of the table is rsCourses.MoveNext
a parameter for the OpenRecordset method). Loop
• This code is explained in Figure 14.3.
FIGURE 14.3: Create a program to loop through the records in a Recordset object.
14.3.4 Using the FindFirst method WHERE condition (a string) that ensures that only one
In this section, you will use the FindFirst method record is found.
of Recordset objects to lookup a specific value in a For example, to get the Title of COMM 351 from
table. the Courses table, you would provide MyLookUp()
• Create a new function called MyLookUp() using with the following parameters:
the following declaration: 1. “Title” — a string containing the name of the
Function MyLookUp(strField As field from which we want to return a value;
String, strTable As String, 2. “Course” — a string containing the name of the
strWhere As String) As String source table; and,
An example of how you would use this function is to 3. “DeptCode = ‘COMM’ AND CrsNum =
return the Title of a course from the Courses ‘335’” — a string that contains the entire
table with a particular DeptCode and CrsNum. In WHERE clause for the search.
other words, MyLookUp() is essentially an SQL
statement without the SELECT, FROM and WHERE Note that both single and double quotation
clauses. marks must be used to signify a string within a
The parameters of the function are used to specify string. The use of quotation marks in this
the name of the table (a string), the name of the field manner is consistent with standard practice in
(a string) from which you want the value, and a English. For example, the sentence:
“He shouted, ‘Wait for me.’” illus-
trates the use of single quotes within double Recordset object to be opened (the Find-
quotes. First method only works with “dynaset” type
recordsets, hence the need to include the
• Define the MyLookUp() function as follows: additional parameter in this segment of code).
Dim dbCurr As DATABASE
Set dbCurr = CurrentDb rsRecords.FindFirst strWhere
If you are using version 2.0, you cannot use VBA uses a rather unique convention to
the CurrentDb method to return a pointer to determine whether to enclose the arguments
the current database. You must use long form of a function, subroutine, or method in paren-
(i.e., Set dbCurr = DBEngine…) theses: if the procedure returns a value,
enclose the parameters in parentheses; oth-
Dim rsRecords As Recordset erwise, use no parentheses. For example, in
Set rsRecords = the line above, strWhere is a parameter of
dbCurr.OpenRecordset(strTable, the FindFirst method (which does not
dbOpenDynaset)
return a value).
• Square brackets [ ] — Square brackets are not x = COMM means that the variable x is equal to
a universally defined programming construct like the value of the variable COMM.
round brackets. As such, square brackets have a • Single quotation marks ‘ ’ — Single quotation
particular meaning in Access/VBA and this marks have only one purpose: to replace normal
meaning is specific to Microsoft products. Simply quotation marks when two sets of quotation
put, square brackets are used to signify the name marks are nested. For example, the expression
of a field, table, or other object in the DAO hierar- x = “[ProductID] = ‘123’” means that the
chy—they have no other meaning. Square brack- variable x is equal to the string ProductID =
ets are mandatory when the object names “123”. In other words, when the expression is
contain spaces, but optional otherwise. For evaluated, the single quotes are replaced with
example, [Forms]![frmCourses]![Dept- double quotes. If you attempt to nest two sets of
Code] is identical to Forms!frm- double quotation marks (e.g., x = “[Produc-
Courses!DeptCode. tID] = “123””) the meaning is ambiguous
• Quotation marks “ ” — Double quotation marks and Access returns an error.
are used to distinguish literal strings from names • The Ampersand & — The ampersand is the con-
of variables, fields, etc. For example, catenation operator in Access/VBA and is unique
x = “COMM” means that the variable x is equal to Microsoft products. The concatenation opera-
to the string of characters COMM. In contrast, tor joins two strings of text together into one
string of text. For example,
x = “one” & “_two” means that the variable • Create a calculated field called Title using the
x is equal to the string one_two. following expression (see Figure 14.5):
If you understand these constructs at this point, then Title: DLookUp(“Title”, “Courses”,
understanding the DLookUp() function is just a mat- “DeptCode = ‘”& [DeptCode] & “’ AND
ter of putting the pieces together one by one. CrsNum = ‘” & [CrsNum] & “’”)
14.3.5.2 Understanding the WHERE clause
14.3.5.1 Using DLookUp() in queries
The first two parameters of the DLookUp() are
The DLookUp() function is extremely useful for per-
straightforward: they give the name of the field and
forming lookups when no relationship exists between
the table containing the information of interest. How-
the tables of interest. In this section, you are going to
ever, the third argument (i.e., the WHERE clause) is
use the DLookUp() function to lookup the course
more complex and requires closer examination.
name associated with each section in the Sections
table. Although this can be done much easier using a At its core, this WHERE clause is similar to the one
join query, this exercise illustrates the use of vari- you created in Section 5.3.2 in that it contains two
ables in function calls. criteria. However, there are two important differ-
• Create a new query called qryLookUpTest ences:
based on the Sections table. 1. Since it is a DLookUp() parameter, the entire
• Project the DeptCode, CrsNum, and Section clause must be enclosed within quotation marks.
fields. This means single and double quotes-within-
quotes must be used.
table
Create a query based on the Sections
only (do not include Courses). correct
Use the DLookUp() function to get the
course title for each section.
2. It contains variable (as opposed to literal) criteria. • Use strWhere in a DLookUp() call.
For example, [DeptCode] is used instead of
“COMM”. This makes the value returned by the 14.4 Discussion
function call dependent on the current value of
the DeptCode field. 14.4.1 VBA versus SQL
In order to get a better feel for syntax of the function The PrintRecords procedure you created in
call, do the following exercises (see Figure 14.6): Section 14.3.3 is interesting since it does essentially
Switch to the debug window and define two string the same thing as a select query: it displays a set of
variables (see Section 12.3.1 for more information records.
on using the debug window): You could extend the functionality of the Print-
strDeptCode = “COMM” Records subroutine by adding an argument and an
strCrsNum = “351” IF-THEN condition. For example:
These two variables will take the place the field val- Sub PrintRecords(strDeptCode as
ues while you are in the debug window. String)
• Write the WHERE clause you require without the Do Until rsCourses.EOF
variables first. This provides you with a template If rsCourses!DeptCode = strDeptCode
Then
for inserting the variables.
• Assign the WHERE clause to a string variable Debug.Print rsCourses!DeptCode & “ ”
& rsCourses!CrsNum
called strWhere (this makes it easier to test).
values
Create string variables that refer to valid
of DeptCode and CrsNum. criteria
Write the WHERE clause using literal
first to get a sense of what is
required.
third
To save typing, use strWhere as the
parameter of the DLookUp()
call.
14.5 Application to the assignment As a result, it is possible to cheat a little bit and cre-
ate a stand-alone table (e.g., SystemVariables)
14.5.1 Using a separate table to store that contains a single record:
system parameters VariableName Value
When you calculated the tax for the order in GST 0.07
Section 9.5, you “hard-coded” the tax rate into the
form. If the tax rate changes, you have to go through
Of course, other system-wide variables could be
all the forms that contain a tax calculation, find the
contained in this table, but one is enough for our pur-
hard-coded value, and change it. Obviously, a better
poses. The important thing about the SystemVari-
approach is to store the tax rate information in a
ables table is that it has absolutely no relationship
table and use the value from the table in all form-
with any other table. As such, you must use a
based calculations.
DLookUp() to access this information.
Strictly speaking, the tax rate for each product is a • Create a table that contains information about the
property of the product and should be stored in the tax rate.
Products table. However, in the wholesaling envi- • Replace the hard-coded tax rate information in
ronment used for the assignment, the assumption is your application with references to the value in
made that all products are taxed at the same rate. the table (i.e., use a DLookUp() in your tax cal-
culations). Although the SystemVariables
table only contains one record at this point, you
should use an appropriate WHERE clause to The reason you must use a DLookUp() to get this
ensure that the value for GST is returned (if no information is that there is no relationship between
WHERE clause is provided, DLookUp() returns the OrderDetails and BackOrders tables.
the first value in the table).
Any relationship that you manage to create
The use of a table such as SystemVari- between OrderDetails and BackOrders
ables contradicts the principles of relational will be nonsensical and result in a non-updat-
database design (we are creating an attribute able recordset.
without an entity). However, trade-offs
between theoretical elegance and practicality • In the query underlying your OrderDetails
are common in any development project. subform, create a calculated field called QtyOn-
BackOrder to determine the number of items on
14.5.2 Determining outstanding backorder for each item added to the order. This
backorders calculated field will use the DLookUp() function.
An good example in your assignment of a situation There are two differences between this DLookUp()
requiring use of the DLookUp() is determining the and the one you did in Section 14.3.5.1
backordered quantity of a particular item for a partic- 1. Both of the variables used in the function (e.g.,
ular customer. You need this quantity in order to cal- CustID and ProductID) are not in the query.
culate the number of each item to ship. As such, you will have to use a join to bring the
missing information into the query. zero. To do this, use the iif() and IsNull()
2. ProductID is a text field and the criteria of text functions, e.g.:
fields must be enclosed in quotation marks, e.g.: QtyOnBackOrderNoNull:
ProductID = “123” iif(IsNull([QtyOnBackOrder]),0,[Qty
However, CustID is a numeric field and the crite- OnBackOrder])
ria for numeric fields is not enclosed in quotations • Use this “clean” version in your calculations and
marks, e.g.: on your form.
CustID = 4.
It is possible to combine these two calculated
Not every combination of CustID and Pro- fields into a one-step calculation, e.g.:
ductID will have an outstanding backorder. iif(IsNull(DLookUp(…)),0,
When a matching records is not found, the DLookUp(…)).
DLookUp() function returns a special value: The problem with this approach is that the
Null. The important thing to remember is DLookUp() function is called twice: once to
that Null plus or minus anything equals test the conditional part of the immediate if
Null. This has implications for your “quantity statement and a second time to provide the
to ship” calculation. “false” part of the statement. If the Back-
Orders table is very large, this can result in
• Create a second calculated field in your query to an unacceptable delay when displaying data
convert any Nulls in the first calculated field to in the form.
cute functions (not subroutines) you must do one of 'this function calls the
two things before you create the macro: ParameterTest subroutine
ParameterTest intStart, intStop
1. Convert ParameterTest to a function — you do
this simply by changing the Sub at the start of the ParameterTestWrapper = True
'return a value
procedure to Function.
End Function
2. Create a new function that executes Parame-
• Call the function, as shown in Figure 15.1.
terTest and call the function from the macro.
15.3.1.1 Creating a wrapper Note that the return value of the function is
Since the second alternative is slightly more interest- declared as an integer, but the actual assign-
ment statement is ParameterTestWrap-
ing, it is the one we will use.
• Open your basTesting module from per = True. This is because in Access/
VBA, the constants True and False are
Tutorial 12.
• Create a new function called ParameterTest- defined as integers (-1 and 0 respectively).
Wrapper defined as follows:
15.3.1.2 Using the RunCode action
Function • Leave the module open (you may have to resize
ParameterTestWrapper(intStart As
and/or move the debug window) and create a
Integer, intStop As Integer) As
new macro called mcrRunCodeTest.
Integer
the
Create a function to call
ParameterTest
subroutine.
• Add the RunCode action and use the expression • Select Run > Start to execute the macro as
builder to select the correct function to execute, shown in Figure 15.3.
as shown in Figure 15.2.
15.3.2 Using activity information to
The expression builder includes two parame- determine the number of credits
ter place holders (<<intStart>> and
In this section, you will create triggers attached to the
<<intStop>>) in the function name. These
After Update event of bound controls.
are to remind you that you must pass two
parameters to the ParameterTestWrap- 15.3.2.1 Scenario
per() function. If you leave the place holders Assume that each type of course activity is generally
where they are, the macro will fail because associated with a specific number of credits, as
Access has not idea what <<intStart>> shown below:
and <<intStop>> refer to.
Activity Credits
• Replace the parameter place holders with two lecture 3.0
numeric parameters (e.g. 3 and 6). Note that in lab 3.0
general, the parameters could be field names or
tutorial 1.0
any other references to Access objects contain-
seminar 6.0
ing (in this case) integers.
FIGURE 15.2: Use the expression builder to select the function to execute.
action
Add a RunCode
to the macro.
down
Use the expression builder to drill
to the user-defined functions in
your database file.
parameter
Replace the
place
question is the following:
1. Look up the default number of credits associated
holders.
with the course activity showing in the form’s
Activity field.
the
Select Run > Start (or press the ! icon in
tool bar) to execute the macro.
2. Copy this number into the Courses.Credits
field.
Create a new record for a lecture-based ofCreate a macro to find the default number
credits and copy the value it into the
course: COMM 437: Database Technology Credits field.
There are several possible answers to the “when” • Ensure that you have a courses form (e.g., frm-
question (although some are better than others). For Courses) and that the form has a combo box for
example: the Activity field. You may wish to order the
1. When the user enters the Credits field (the On fields such that Activity precedes Credits in
Enter event for Credits) — The problem with the tab order (as shown in Figure 15.4).
this choice is that the user could modify the
If your move fields around, remember to
course’s activity without moving the focus to the
adjust the tab order accordingly (recall
Activity field. In such a case, the trigger would
Section 8.3.4).
not execute.
2. When the user changes the Activity field (the 15.3.2.4 Looking up the default value
After Update event for Activity) — This choice
As you discovered in Section 14.3.5, Access has a
guarantees that whenever the value of Activ-
DLookUp() function that allows you to go to the
ity is changed, the default value will be copied
Activities table and find the value of Credits
into the Credits field. As such, it is a better
for a particular value of Activity. A different
choice.
approach is to join the Activities table with the
15.3.2.3 Preliminary activities Courses table in a query so that the default value of
• Modify the Activities table to include a single- credits is always available in the form. This is the
precision numeric field called Credits. Add the approach we will use here.
values shown in the table in Section 15.3.2.1.
change
Bring up the form’s property list and
its Record Source property.
ules that run when the form is open.
15.3.2.6 Creating the SetValue macro
The SetValue macro you require here is extremely
simple once you have DefaultCredits available
within the scope of the form.
• Create the mcrCourses.SetCredits macro
as shown in Figure 15.8.
15.3.2.7 Attaching a procedure to the After
Update event
The On Click event of a button is fairly simple to
understand: the event occurs when the button is
clicked. The events associated with non-button
objects operate in exactly the same way. For exam-
ple, the After Update event for controls (text box,
The field list now contains all
the fields in the new query. combo box, check box, etc.) occurs when the value
• Move to the field on which you want to search • Set the Enabled property of DeptCode to No (the
(e.g., DeptCode); user should never be able to change the key val-
• Select Edit > Find (or press Control-F); ues of existing records).
• Fill out the search dialog box as shown in
15.3.3.3 Creating the unbound combo box
Figure 15.9.
The key thing to remember about the combo box
In the dialog box, you specify what to search for used to specify the search criterion is that it has
(usually a key value) and specify how Access should
nothing to do with the other fields or the underlying
conduct its search. When you press Find First, table. As such, it should be unbound.
Access finds the first record that matches your
• Create an unbound combo box in the form
search value and makes it the current record (note
header, as shown in Figure 15.10.
that if you are searching on a key field, the first • Change the Name property of the combo box to
matching record is also the only matching record).
cboDeptCode.
15.3.3.2 Preliminaries • The resulting combo box should resemble that
To make this more interesting, assume that the frm- shown in Figure 15.11.
Departments form is for viewing editing existing
When you create an unbound combo box,
departmental information (rather than adding new
Access gives it a default name (e.g.,
departments). To enforce this limitation, do the fol-
Combo5). You should do is change this to
lowing:
something more descriptive (e.g., cboDept-
• Set the form’s Allow Additions property to No.
FIGURE 15.9: Search for a record using the “find” dialog box.
the
Move the cursor to
field you wish to
search and invoke
the search box
using Control-F.
and
Enter the value you wish to find
set the other search
parameters as required.
(or
Press Find First to move to the first
only) record that matches the
search condition.
down
Drag the separator for the detail
to make room in the form
header
selecting
Create an unbound combo box by
the combo box tool and
clicking in the header area.
FIGURE 15.12: Use the builder to specify the name of the property to set.
expression
To set the Item argument, use the
builder to drill down
to the correct form.
(cboDeptCode)
Select the unbound combo box
from the middle
pane. A list of properties for the
selected object is displayed in the
pane on the right.
Access interprets any text in the Find What 15.3.4 Using Visual Basic code instead of
argument as a literal string (i.e., quotation a macro
marks would not be required to find COMM). To Instead of attaching a macro to the After Update
use an expression (including the contents of a
event, you can attach a VBA procedure. The VBA
control) in the Find What argument, you must procedure is much shorter than its macro counter-
precede it with an equals sign (e.g.,
part:
=[cboDeptCode].
1. a copy (clone) of the recordset underlying the
• You cannot disable a control if it has the focus. form is created,
Therefore, include another GotoControl action 2. the FindFirst method of this recordset is used
to move the cursor to cboDeptCode before set- to find the record of interest.
ting DeptCode.Enabled = No. 3. the “bookmark” property of the clone is used to
• Attach the macro mcrSearch.FindDepart- move to the corresponding bookmark for the
ment to the After Update event of the cboDept- form.
Code combo box. To create a VBA search procedure, do the following:
• Test the search feature. • Change the After Update event of cboDeptCode
to “Event Procedure”.
• Press the builder ( ) to create a VBA subrou-
tine.
• Enter the two lines of code below, as shown in a non-human-readable data type and therefore is
Figure 15.14. not of much use unless it is used in the manner
Me.RecordsetClone.FindFirst shown here. Setting the Bookmark property of a
“DeptCode = ‘” & cboDeptCode & “'” record makes the record with that bookmark the
Me.Bookmark = current record. In the example above, the book-
Me.RecordsetClone.Bookmark mark of the records underlying the form is set to
This program consists of a number of interesting ele- equal the bookmark of the clone. Since the clone
ments: had its bookmark set by the search procedure,
• The property Me refers to the current form. You this is equivalent to searching the recordset
can use the form's actual name, but Me is much underlying the form.
faster to type.
• A form’s RecordsetClone property provides a 15.4 Application to the assignment
means of referencing a copy of the form's under-
lying recordset. 15.4.1 Triggers to help the user
• The FindFirst method is straightforward. It • Create a trigger on your order form that sets the
acts, in this case, on the clone. actual selling price of a product to its default
• Every recordset has a bookmark property that price. This allows the user to accept the default
uniquely identifies each record. A bookmark is price or enter a new price for that particular trans-
like a “record number”, except that it is stored as action (e.g., the item could be damaged). You will
FIGURE 15.14: Implement the search feature using a short VBA procedure.
reference
Change the After Update event to
an event procedure.
editor.
Press the builder button to invoke the VBA
have to think carefully about which event to Section 10.4). The problem is updating the Back-
attach this macro to. Orders table itself because two different situations
• Create a trigger on your order form that calcu- have to be considered:
lates a suggested quantity to ship and copies this 1. A record for the particular customer-product
value into the quantity to ship field. The sug- combination exists in the BackOrders table --
gested value must take into account the amount If a backorder record exists for a particular cus-
ordered by the customer, any outstanding backo- tomer and a particular product, the quantity field
rders for that item by that customer, and the cur- of the record can be added-to or subtracted-from
rent quantity on hand (you cannot ship what you as backorders are created and filled.
do not have). The user should be able to override 2. A customer-product record does not exist in
this suggested value. (Hint: use the MinValue() the BackOrders table -- If the particular cus-
function you created in Section 12.5.) tomer has never had a backorder for the product
• Provide you customer and products forms with in question, then there is no record in the Back-
search capability. Orders table to update. If you attempt to update
a nonexistent record, you will get an error.
15.4.2 Updating the BackOrders table
What is required, therefore, is a means of determin-
Once a sales order is entered into the order form, it ing whether a record already exists for a particular
is a simple matter to calculate the amount of each customer-product combination. If a record does
product that should be backordered (you did this in exist, then it has to be updated; if a record does not
exist, then one has to be created. This is simple (see Section 15.3.2.4 to review renaming
enough to talk about, but more difficult to implement fields in queries).
in VBA. As a result, you are being provided with a
shortcut function called UpdateBackOrders() Note that if the backordered quantity is positive,
that implements this logic. items are backordered. If the backordered quantity is
negative, backorders are being filled. If the backor-
The requirements for using the UpdateBackO-
dered quantity is zero, no change is required and
rders() function are outlined in the following sec-
these records should no be included in the results of
tions:
the query.
15.4.2.1 Create the pqryItemsToBackOrder
15.4.2.2 Import the shortcut function
query
Import the Visual Basic for Applications (VBA) mod-
If you have not already done so, create the pqry-
ule containing the code for the
ItemsToBackOrder query described in
UpdateBackOrders() function. This module is
Section 10.4. The UpdateBackOrders() proce-
contained in an Access database called
dure sets the parameter for the query and then cre-
BOSC_Vx.mdb that you can download from the
ates a recordset based on the results.
course home page.
If you did not use the field names OrderID, • BOSC_V2.mdb is for those running Access ver-
and ProductID in your tables, you must use sion 2.0. To import the module, select File >
the calculated field syntax to rename them
Import, choose BOSC_V2.mdb, and select Mod- ers, and Products. If any of your tables or fields
ule as the object type to import. are named differently, an error occurs. To eliminate
• BOSC_V7.mdb is for those running Access ver- these errors, you can do one of two of things:
sion 7.0 or higher. To import the module, select 1. Edit the VBA code. Use the search-and-replace
File > Get External Data > Import, choose feature of the module editor to replace all
BOSC_V7.mdb, and select Module as the object instances of field names in the supplied proce-
type to import. dures with your own field names. This is the rec-
15.4.2.3 Use the function in your application ommended approach, although you need an
adequate understanding of how the code works
The general syntax of the function call is:
UpdateBackOrders(OrderID, CustomerID). in order to know which names to change.
2. Change the field names in your tables (and all
The OrderID and CustomerID are arguments and
queries and forms that reference these field
they both must be of the type Long Integer. If this names). This approach is not recommended.
function is called properly, it will update all the backo-
rdered items returned by the parameter query. 15.4.3 Understanding the
15.4.2.4 Modifying the UpdateBackOrders() UpdateBackOrders() function
function The flowchart for the UpdateBackOrders() func-
The UpdateBackOrders() function looks for spe- tion is shown in Figure 15.15. This function repeat-
cific fields in three tables: BackOrders, Custom- edly calls a subroutine, BackOrderItem, which
updates or adds the individual items to the BackO- FIGURE 15.15: Flowchart for
rders table. The flowchart for the BackOrderItem UpdateBackOrders().
subroutine is shown in Figure 15.16.
There are easier and more efficient ways of imple- start
stop
start
no no
check Customer table to valid? error message stop
ensure valid CustID
yes
stop
15.4.4 Annotated source code for the MsgBox “Back order cannot be processed:
backorders shortcut module. order contains no items”
Exit Sub
In the following sections, the two procedures in the
End If
shortcut module are examined. In each case, the
Do Until rsBOItems.EOF
code for the procedure is presented followed by
Call BackOrderItem(lngCustID,
comments on specific lines of code.
rsBOItems!ProductID, rsBOItems!Qty)
15.4.4.1 The UpdateBackOrders() function rsBOItems.MoveNext
Function UpdateBackOrders(ByVal Loop
lngOrdID As Long, ByVal lngCustID As rsBOItems.Close
Long) End Function
Set dbCurr = CurrentDb
15.4.4.2 Explanation of the
Dim rsBOItems As Recordset UpdateBackOrders() function
dbCurr.QueryDefs!pqryItemsToBackOrder.
Parameters!pOrderID = lngOrdID Function UpdateBackOrders(ByVal lngOr-
Set rsBOItems = dID As Long, ByVal lngCustID As Long) —
dbCurr.QueryDefs!pqryItemsToBackOrder This statement declares the function and its parame-
.OpenRecordset() ters. Each item in the parameter list contains three
If rsBOItems.RecordCount = 0 Then elements: ByVal or ByRef (optional), the variable's
name, and the variable's type (optional). The ByVal
keyword simply means that a copy of the variables Dim rsBOItems As Recordset — In this decla-
value is passed the subroutine, not the variable ration statement, a pointer to a Recordset object is
itself. As a result, variables passed by value cannot declared. This recordset contains a list of all the
be changed by the sub-procedure. In contrast, if a items to add to the BackOrders table.
variable is passed by reference (the default), its dbCurr.QueryDefs!pqryItemsToBackOrder
value can be changed by the sub-procedure. .Parameters!pOrderID = lngOrdID — This
Set dbCurr = CurrentDb — Declaring a vari- one is a bit tricky: the current database (dbCurr)
able and setting it to be equal to something are dis- contains a collection of objects called QueryDefs
tinct activities. In this case, the variable dbCurr (these are what you create when you use the QBE
(which is declared in the declarations section) is set query designer). Within the collection of QueryDefs,
to point to a database object. Note that the database there is one called pqryItemsToBackOrder
object is not created, it already exists. (which you created in Section 15.4.2.1).
CurrentDb is a function supported in Access ver- Within every QueryDef, there is a collection of zero
sion 7.0 and higher that returns a reference to the or more Parameters. In this case, there is one called
current database. In Access version 2.0, this function pOrderID and this sets the value of the parameter
does not exist and thus the current database must to the value of the variable lngOrderID (which was
be found by starting at the top level object in the passed to the function as a parameter).
Access DAO hierarchy, as discussed in Set rsBOItems = dbCurr.QueryDefs!pqry-
Section 14.3.1. ItemsToBackOrder.OpenRecordset() — Here
is another set statement. In this one, the variable MsgBox “Back order cannot be processed:
rsBOItems is set to point at a recordset object. order contains no items” — The MsgBox
Unlike the current database object above, however, statement pops up a standard message box with an
this recordset does not yet exist and must be created Okay button in the middle.
by running the pqryItemsToBackOrder parame- Exit Sub — If this line is reached, the list contains
ter query. no items. As such, there is no need to go any further
OpenRecordset is a method that is defined for in this subroutine.
objects of type TableDef or QueryDef that creates an End If — The syntax for If… Then… Else… state-
image of the data in the table or query. Since the ments requires an End If statement at the end of
query in question is a parameter query, and since the the conditional code. That is, everything between the
parameter query is set in the previous statement, the If and the End If executes if the condition is true;
resulting recordset consists of a list of backordered otherwise, the whole block of code is ignored.
items with an order number equal to the value of
Do Until rsBOItems.EOF — The EOF property
pOrderID.
of a recordset is set to true when the “end of file” is
If rsBOItems.RecordCount = 0 Then — The encountered.
only thing you need to know at this point about the
Call BackOrderItem(lngCustID, rsBOI-
RecordCount property of a recordset is that it returns
tems!ProductID, rsBOItems!Qty) — A sub-
zero if the recordset is empty.
routine is used to increase the modularity and
readability of this function. Note the way in which the 15.4.4.3 The BackOrderItem() subroutine
current values of ProductID and Qty from the Sub BackOrderItem(ByVal lngCustID As
rsBOItems Recordset are accessed. Long, ByVal strProdID As String, ByVal
rsBOItems.MoveNext — MoveNext is a method intQty As Integer)
Set dbCurr = CurrentDb
defined for recordset objects. If this is forgotten, the
EOF condition will never be reached and an infinite Dim strSearch As String
Dim rsBackOrders As Recordset
loop will be created. In VBA, the Escape key is usu-
Set rsBackOrders =
ally sufficient to stop an infinite loop.
dbCurr.OpenRecordset(“BackOrders”,
Loop — All Do While/Do Until loops must end dbOpenDynaset)
with the Loop statement. strSearch = “CustID = “ & lngCustID & “
rsBOItems.Close — When you create a new AND ProductID = '" & strProdID & “'”
object (such as a Recordset using the Open- rsBackOrders.FindFirst strSearch
Recordset method), you should close it before exit- If rsBackOrders.NoMatch Then
ing the procedure. Note that you do not close Dim rsCustomers As Recordset
dbCurr because you did not open it. Set rsCustomers =
dbCurr.OpenRecordset(“Customers”,
End Function — All functions/subroutines need dbOpenDynaset)
an End Function/End Sub statement. strSearch = “CustID = “ & lngCustID
rsCustomers.FindFirst strSearch
a table name, a query name, or an SQL statement. do this, single quotes are used within the search
The dbOpenDynaset argument is a predefined con- string.
stant that tells Access to open the recordset as a rsBackOrders.FindFirst strSearch —
dynaset. You don't need to know much about this FindFirst is a method defined for Recordset
except that the format of these predefined constants objects that finds the first record that meets the crite-
is different between Access version 2.0 and version ria specified in the method's argument. Its argument
7.0 and higher. In version 2.0, constants are of the is the text string stored in strSearch.
form: DB_OPEN_DYNASET.
If rsBackOrders.NoMatch Then — The
strSearch = “CustID = ”& lngCustID & “ NoMatch property should always be checked after
AND ProductID = ’” & strProdID & “'” — searching a record set. Since it is a Boolean variable
A string variable has been used to break the search (True / False) it can be used without an comparison
process into two steps. First, the search string is operator.
constructed; then the string is used as the parameter
rsBackOrders.AddNew — Before information can
for the FindFirst method. The only tricky part here
be added to a table, a new blank record must be cre-
is that lngCustID is a long integer and strProdID
ated. The AddNew method creates a new empty
is a string. The difference is that the value of str-
record, makes it the active record, and enables it for
ProdID has to be enclosed in quotation marks when
editing.
the parameter is passed to the FindFirst method. To