JET Database/Data integrity
JET (along with its major application wrapper, Microsoft Access) is often blamed for having poor data integrity, but this is most often because few or none of the data integrity features of the database have been properly used.
The JET database supports many of the standard data integrity functions that are expected of relational databases, including constraints, transactions, and locking. This support is something that has evolved over time, with only JET 4.0 supporting some features.
There are also some locking and buffering problems associated with using JET databases in multi-user environments, and especially so over local area networks (LAN). Such problems may be the subject of another chapter, however.
Primary keys help to define a set of columns for a table, that can be used to uniquely identify each row. No two rows can have the same values in the columns that make up the primary key, a constraint that the database enforces by refusing to insert a row when there already exists a row with those values in the primary key columns.
Unique indexes and unique constraints
JET supports both unique indexes and unique constraints, subtly different concepts that achieve essentially the same functionality. A unique index is an index that cannot have duplicate values for the columns in the index, whereas a unique constraint is a data integrity rule that prevents rows being inserted with the same values in the columns listed in the constraint. Both can can used to implement the logical data model concept of an alternate key.
The JET engine implements the concept of the unique constraint by creating a unique index. A unique constraint can be added to a table in the
Create Table statement, or via an
Alter Table statement.
JET supports the foreign key constraint, allowing entity-relationship data modelling rules to be enforced at the database level. A foreign key constraint prevents rows from being inserted when no corresponding row exists in a related table, and also prevents rows from being deleted when related tables have dependent rows referencing them.
The JET engine automatically creates an index on the columns that compose the foreign key.
A foreign key constraint must reference all columns in a primary key, unique constraint, or unique index on the referenced table. The code below shows a foreign key referencing a unique constraint (e.g. an alternate key) on another table.
CREATE TABLE F3 ( id INT IDENTITY(1, 1) NOT NULL, a INT NOT NULL, b VARCHAR(20) NOT NULL, c VARCHAR(20) NOT NULL, CONSTRAINT F3_pk PRIMARY KEY (id), CONSTRAINT F3_uc UNIQUE (a, b) ) GO CREATE TABLE F4 ( i INT NOT NULL, a INT NOT NULL, b VARCHAR(20) NOT NULL, CONSTRAINT F4_pk PRIMARY KEY (i), CONSTRAINT F4_fk1 FOREIGN KEY (a, b) REFERENCES F3 (a, b) ) GO
JET 4.0 introduced cascading updates and deletes to foreign keys. When a foreign key is created with
Update Cascade, the foreign keys are updated if the referenced columns are changed.
Delete Cascade causes the referencing rows to be deleted if the referenced row is deleted, and
Delete Set Null sets the foreign keys to Null if the referenced row is deleted.
CREATE TABLE F5 ( i INT NOT NULL, a INT NOT NULL, b VARCHAR(20) NOT NULL, CONSTRAINT F5_pk PRIMARY KEY (i), CONSTRAINT F5_fk1 FOREIGN KEY (a, b) REFERENCES F3 (a, b) ON UPDATE Cascade ON DELETE SET NULL ) GO
JET 4.0 introduced check constraints, which apply additional logic in the data integrity of the database. A check constraint is an expression that further constrains the allowable values in a column. The expression can be a simple value bounding validation, or it can include a sub-query that references values in other tables.
Check constraints can be useful for more than just validating input values. The following example shows how a check constraint can ensure that a table contains only one row.
CREATE TABLE Singleton ( ID CHAR(1) NOT NULL, a VARCHAR(20), ... CONSTRAINT Singleton_PK PRIMARY KEY (ID), CONSTRAINT Singleton_One_Row CHECK(ID = 'A') ) GO
From JET 4.0, JET supports transactions for multiple statements, giving developers the ability to write robust code that updates the database without compromising logical consistency by, for example, allowing half an invoice to be created, or half a client's records to be updated. Thus, statements within a declared transaction will succeed or fail together.
A transaction must be explicitly created by issuing the
Begin Transaction statement. Subsequent statements will not be committed to the database until a
Commit Transaction statement is issued. If a
Rollback Transaction statement is issued, all statements since the transaction began will fail, i.e. none of them will be committed to the database.
BEGIN TRANSACTION GO INSERT INTO U1 (a, b, c) VALUES (1, 'First', 'Row') GO ROLLBACK TRANSACTION GO SELECT * FROM U1 GO
(0 row(s) returned)
When using Microsoft's ADO database components, transactions are typically managed through the BeginTrans / CommitTrans / RollbackTrans methods on those objects. However, they can just as easily be implemented through statement execution, as shown above.
JET supports read and write locks on the database, either through exclusive access, or shared access. Locks can be set at the row level, page level, or database level.
From JET 4.0, Row level locks will be automatically promoted to page or table level when the number of rows locked reaches a threshold. This threshold is set in the Windows registry entry
PagesLockedToTableLock found under the key
Locking can be configured by setting the isolation level on the database connection. For ADO, this is done with the
IsolationLevel property on the