Declarative referential integrity versus triggers

By Claudio Valderrama - Copyright www.cvalde.net

Relational theory says you must normalize data to make it fit inside a relational model. Of course, entities must be connected in some way. The “glue” or link between two normalized entities is a common field for both entities. The common field acts as a bridge so you can pass from one entity to another using SQL and it's the physical representation of a logical “relationship” between two entities. Indeed, you can have one entity linked with itself or three entities linked at the same time too, depending on the semantics of your model.

When a model is normalized, this means usually you get one real-world concept or thing decomposed in several normalized entities, usually referred as tables. But these tables must be kept in sync to get a consistent data repository. For example, a bill and its details are two separate tables, but no detail should be allowed if it refers to a bill that doesn't exist. Contrarily, a bill cannot be deleted unless there aren't any details or these details are erased first. Validating these rules in client applications is not the bulletproof solution because people might be able to use a third party tool to insert, delete or update data. So referential integrity comes to the rescue. This is a metadata declaration that enforces a dependency relationship between two tables through one or more fields. So, the engine itself is in charge of verifying consistency across tables; you can think that one table “refers” to another for validation and hence the name referential integrity.

In Firebird/InterBase®, a referential integrity constraint can be set at a field level or at a table level. When more than one field is involved, a table-level constraint is required. In SQL, this constraint is called a foreign key (FK) declaration. The lookup table (the one where values are verified against) must exist. Three important restrictions are:

Now that FK declarations have established the database schema, you will find that's not very easy to modify metadata of tables with referential integrity constraints. Specifically, you cannot drop or alter fields that are involved in a FK declaration. Because of these limitations, you may be tempted to define your referential integrity not with DDL standard commands but handle the validations for yourself by means of triggers. However, some drawbacks exist:

In Firebird/InterBase®, stored procedures and triggers cannot start transactions by themselves and cannot change the current transaction isolation. They run inside the transaction started by the client. You can say that procedural referential integrity (namely, using triggers and not declarations) still can be used provided that the READ COMMITTED level of isolation is used by all the client. After all, other relational engines used read committed as the default level and the BDE uses it, too. However, nothing prevents your client from using snapshot explicitly (even on other engines) and you have no way to detect that in a trigger. Also, one aim of relational engines is passing the enforcement of all basic rules to the engine, so there's no need to reinvent the wheel unless you really need a very complex lookup in another table. In this case, it can be a sign of a normalization problem.