3. Understanding disabled, enabled, and trusted constraints
Not all types of constraints behave in the same way. UNIQUE and PRIMARY KEY constraints always ensure that all the data is valid with respect to them but a FOREIGN KEY or CHECK constraint can exist in the database in one of three states:
disabled – exists in the database, is exposed via system views, and can be scripted out; but does not do anything
enabled but not trusted – validates all modifications, but does not guarantee that all existing data conforms to its rules
enabled and trusted – validates all modifications, and guarantees that all existing data is valid.
Note that only CHECK constraints and FOREIGN KEY constraints can be disabled. Under the hood PRIMARY KEY and UNIQUE constraints are implemented as UNIQUE indexes. As such, neither PRIMARY KEY nor UNIQUE constraints can be disabled, and will always be trusted.
We can demonstrate the differences between disabled, enabled, and trusted constraints with a simple example, involving a FOREIGN KEY constraint. Listing 13 drops and recreates our Boxes table and then creates a child table, Items.
Listing 14 populates each table with some valid test data, as per our FK_Items_Boxes constraint.
Listing 15 confirms that the constraint prevents invalid data from saving.
Disabled constraints do...nothing
Say we need to bulk load data into these two tables,
and that the loading must complete as quickly as possible; this is a
very common requirement. Assuming we know that the data comes from a
trusted source, and that all the data in that source is clean, it is
quite a common practice to disable the FOREIGN KEY constraint,
so that we can start loading data into both tables simultaneously
without having to make sure that parent rows load before their child
ones.
Disabling the constraint is shown in Listing 16.
We can confirm that the constraint is disabled, by running the query in Listing 17.
To determine if a CHECK constraint is disabled...
...we need to query another system view, named sys.check_constraints.
|
Our disabled constraint no longer prevents us from saving orphan rows. If we rerun Listing 14 it will succeed, and an orphan row will be inserted into the child table, as shown in Listing 18.
In short, a disabled constraint does not actually do anything, although it still exists, and you can still script it out.
Enabled constraints do not validate existing data
For the following reasons, it is quite possible that, at the end of our bulk load, the row inserted by Listing 6-18 will still be an orphan:
the source of our data has failed to maintain data integrity
parent rows were exported before child ones, so the data being loaded does not represent a consistent point-in-time snapshot
our load has partially failed.
Once the bulk load has finished, we can re-enable the FOREIGN KEY constraint, as shown in Listing 19.
Having successfully re-enabled the constraint (rerun Listing 17 to verify), we cannot insert any more invalid data.
However, we still have an orphan row in the Items table. In short, enabled constraints prevent entering invalid data, but they do not guarantee that all existing data is valid.
Trusted constraints guarantee that existing data is valid
The problem we have is that when we enabled our FOREIGN KEY constraint, in Listing 19, we did not validate existing data against the constraint, and so SQL Server marks it as "not trusted," as shown in Listing 21.
To perform this validation, simply use the WITH CHECK option when enabling the constraint, as shown in Listing 22. The command fails, as it should, because we have an orphan row.
Let us delete that orphan row, as shown in Listing 23.
Now rerun Listing 22 and it will succeed, and the constraint will now be trusted (which can be verified by rerunning 21).
Only PRIMARY KEY and UNIQUE constraints are always trusted. A CHECK or FOREIGN KEY
constraint may become non-trusted as a result, for example, of
disabling it, and then re-enabling it without validating the existing
data. Even if the data were completely valid, the constraint will still
not be trusted unless the WITH CHECK option is used when it is enabled.
Only when a constraint is trusted can we
know for certain that all the data in the table is valid with respect to
that constraint. An added advantage of trusted constraints is that they
can be used by the optimizer when devising execution plans. Conversely,
the optimizer will ignore non-trusted constraints, meaning that
valuable information will go unused, and could lead to suboptimal plans.