- Why This Chapter Is Important
- Introduction to Relational Database Design
- Establishing Relationships in Access
- Establishing Referential Integrity
- Looking at the Benefits of Relationships
- Examining Indexes and Relationships
- Practical Examples: Establishing the Relationships Between the Tables Included in the Time and Billing Database
As you can see, establishing a relationship is quite easy. Establishing the right kind of relationship is a little more difficult. When you attempt to establish a relationship between two tables, Access makes some decisions based on a few predefined factors:
Access establishes a one-to-many relationship if one of the related fields is a primary key or has a unique index.
Access establishes a one-to-one relationship if both the related fields are primary keys or have unique indexes.
Access creates an indeterminate relationship if neither of the related fields is a primary key, and neither has a unique index. You cannot establish referential integrity in this case.
As covered earlier in the chapter, referential integrity consists of a series of rules that the Jet Engine applies to ensure that it properly maintains the relationships between tables. At the most basic level, referential integrity rules prevent the creation of orphan records in the table on the many side of the one-to-many relationship. After establishing a relationship between a Customers table and an Orders table, for example, all orders in the Orders table must be related to a particular customer in the Customers table. Before you can establish referential integrity between two tables, the following conditions must be met:
The matching field on the one side of the relationship must be a Primary Key field or must have a unique index.
The matching fields must have the same data types (for linking purposes, AutoNumber fields match Long Integer fields). With the exception of Text fields, they also must have the same size. Number fields on both sides of the relationship must have the same size (Long Integer, for example).
Both tables must be part of the same Access database.
Both tables must be stored in the proprietary Access file (.MDB) format (they cannot be external tables from other sources).
The database containing the two tables must be open.
Existing data within the two tables cannot violate any referential integrity rules. All orders in the Orders table must relate to existing customers in the Customers table, for example.
Although Text fields involved in a relationship do not have to be the same size, it is prudent to make them the same size. Otherwise, you will degrade performance as well as risk the chance of unpredictable results when creating queries based on the two tables.
After you establish referential integrity between two tables, the Jet Engine applies the following rules:
You cannot enter a value in the foreign key of the related table that does not exist in the primary key of the primary table. For example, you cannot enter a value in the CustomerID field of the Orders table that does not exist in the CustomerID field of the Customers table.
You cannot delete a record from the primary table if corresponding records exist in the related table. For example, you cannot delete a customer from the Customers table if related records exist in the Orders table (records with the same value in the CustomerID field).
You cannot change the value of a primary key on the one side of a relationship if corresponding records exist in the related table. For example, you cannot change the value in the CustomerID field of the Customers table if corresponding orders exist in the Orders table.
If you attempt to violate any of the previous three rules and you have enforced referential integrity between the tables, Access displays an appropriate error message, as shown in Figure 3.7.Figure 3.7 An error message when attempting to add an order for a customer who doesn't exist.
The Jet Engine's default behavior is to prohibit the deletion of parent records that have associated child records and to prohibit the change of a primary key value of a parent record when that parent has associated child records. You can override these restrictions by using the two check boxes available in the Relationships dialog when you establish or modify a relationship.
The following example enforces referential integrity between the tblCustomers table and the tblOrders table. It illustrates how this affects the process of adding and deleting records.
To open the Relationships window, select the Database window and click Relationships on the toolbar. Double-click the join line between tblCustomers and tblOrders. Enable the Enforce Referential Integrity check box. Click OK. Repeat the process for the relationship between tblOrders and tblOrderDetails.
Go into tblCustomers and add a couple of records. Take note of the customer IDs. Go into tblOrders. Add a couple of records, taking care to assign customer IDs of customers that exist in the tblCustomers table. Now try to add an order for a customer whose customer ID does not exist in tblCustomers. You should get an error message.
Attempt to delete a customer from tblCustomers who does not have any orders. You should get a warning message, but Access should allow you to complete the process. Now try to delete a customer who does have orders. The Jet Engine should prohibit you from deleting the customer. Attempt to change the customer ID of a customer who has orders. You should not be able to do this.
Cascade Update Related Fields
The Cascade Update Related Fields option is available only if you have established referential integrity between the tables. With this option selected, the user can change the primary key value of the record on the one side of the relationship. When the user tries to modify the field joining the two tables on the one side of the relationship, the Jet Engine cascades the change down to the foreign key field on the many side of the relationship. This is useful if the primary key field is modifiable. For example, a purchase number on a purchase order master record might be updatable. If the user modifies the purchase order number of the parent record, you would want to cascade the change to the associated detail records in the purchase order detail table.
There is no need to select the Cascade Update Related Fields option when the related field on the one side of the relationship is an AutoNumber field. You can never modify an AutoNumber field. The Cascade Update Related Fields option has no effect on AutoNumber fields.
It is very easy to introduce a loophole into your system accidentally. If you create a one-to-many relationship between two tables but forget to set the Required property of the foreign key field to Yes, you allow the addition of orphan records. Figure 3.8 illustrates this point. I added an order to tblOrders without entering a customer ID. This record is an orphan record because no records in tblCustomers have a customer ID of Null. To eliminate the problem, set the Required property of the foreign key field to Yes.
Cascade Delete Related Records
The Cascade Delete Related Records option is available only if you have established referential integrity between the tables. With this option selected, the user can delete a record on the one side of a one-to-many relationship, even if related records exist in the table on the many side of the relationship. A user can delete a customer even if the customer has existing orders, for example. The Jet Engine maintains referential integrity between the tables because it automatically deletes all related records in the child table.
If you attempt to delete a record from the table on the one side of a one-to-many relationship and no related records exist in the table on the many side of the relationship, you get the usual warning message, as shown in Figure 3.9. On the other hand, if you attempt to delete a record from the table on the one side of a one-to-many relationship and related records exist in the child table, Access warns you that you are about to delete the record from the parent table, as well as any related records in the child table (see Figure 3.10).
Figure 3.9 A message that appears after the user attempts to delete a parent record without related child records.
Figure 3.10 A message that appears after the user attempts to delete a parent record with related child records.
The Cascade Delete Related Records option is not always appropriate. It is an excellent feature, but you should use it prudently. Although it is usually appropriate to cascade delete from an Orders table to an Order Details table, for example, it generally is not appropriate to cascade delete from a Customers table to an Orders table. This is because you generally do not want to delete all your order history from the Orders table if for some reason you want to delete a customer. Deleting the order history causes important information, such as your profit and loss history, to change. It therefore is appropriate to prohibit this type of deletion and handle the customer in some other way, such as marking him as inactive, or archiving his data. On the other hand, if you delete an order because the customer cancelled it, you probably want to remove the corresponding order detail information as well. In this case, the Cascade Delete Related Records option is appropriate. You need to make the most prudent decision in each situation, based on business needs. The important thing is to carefully consider the implications of each option before making your decision.
With the Cascade Update feature enabled, you are able to update the primary key value of a record that has associated child records. With the Cascade Delete feature enabled, you can delete a parent record that has associated child records. This exercise illustrates the use of Cascade Update and Cascade Delete.
Modify the relationship between tblCustomers and tblOrders. Enable the Cascade Update Related Fields check box. Modify the relationship between tblOrders and tblOrderDetails. Enable the Cascade Delete Related Records check box. There is no need to enable Cascade Update Related Fields because the OrderID field in tblOrders is an AutoNumber field.
Attempt to delete a customer who has orders. The Jet Engine should still prohibit you from doing this because you did not enable Cascade Delete Related Records. Change the customer ID in tblCustomers of a customer who has orders. The Jet Engine should allow this change. Take a look at the tblOrders table. The Jet Engine should have updated the customer ID of all corresponding records in the table to reflect the change in the parent record.
Add some order details to the tblOrderDetails table. Try to delete any order that has details within the tblOrderDetails table. You should receive a warning, but the Jet Engine should allow you to complete the process.