There are five types of constraint in SQL Server, and they fall into three categories - programmable constraints, local key constraints and foreign key constraints.
So let's look at the programmable constraints first. These are default constraints, which provide a simple default value for a column, and check constraints, which validate that the data entered for a column passes business rules.
Default constraints are quite simple, and the meta data is available in [sys].[default_constraints]
. The columns that this gives us over [sys].[objects]
are as follows:
parent_column_id | This is the ID of the column to which the default constraint belongs. You can look up the column using the [parent_object_id] and [parent_column_id] columns to reference [object_id] and [column_id] in [sys].[columns] |
---|---|
definition | This is the actual definition of the default. Note that the definition listed here will be bracketed and white space will be removed - therefore it's not uncommon to see a default value of 0 defined as ((0)). This is worth bearing in mind - your original SQL defintion of a constraint will not necessarily be re-creatable from meta-data. |
is_system_named | This is 1 if the constraint was system named - i.e. it was not given a name when it was defined. System named constraints are best avoided, as they lead to difficulty in matching up schemas between databases, and documentation of them becomes an onerous task. |
So what about check constraints? There's a little more depth to these - the meta data is available in [sys].[check_constraints]
and the columns that this gives us over [sys].[objects]
are as follows:
is_disabled | This is 1 if the constraint has been disabled using ALTER TABLE ... NOCHECK CONSTRAINT . |
---|---|
is_not_for_replication | This is 1 if the constraint was created with the NOT FOR REPLICATION keyword. This effectively means that replicated databases do not enforce the constraint during DML operations. |
is_not_trusted | This is 1 if the constraint hasn't been verified for all rows. This will be 1 if the constraint has been disabled for some time, or was created on an existing table with the WITH NOCHECK keyword. |
parent_column_id | This is much the same as for [parent_column_id] in [sys].[default_constraints] (see above) with the exception that if the value is 0, then the constraint is a check constraint which checks multiple columns, and is therefore a 'table level' check constraint. |
definition | This is the actual definition of the constraint. Note that the same rules apply as for the default constraint definition, and the format of the expression may well be different to the format with which it was created. |
uses_database_collation | This is 1 if the database collation makes a difference when evaluating the check constraint. If it does, then the default collation of the database can't be changed. |
is_system_named | As for [sys].[default_constraints] , with the same caveats (see above). |
So that is the programmable constraints. Pretty straight forward. So what about the two types of key constraint? Well, these are neatly separated in the meta data into the [sys].[key_constraints]
and [sys].[foreign_keys]
columns.
Let's look at [sys].[key_constraints]
first. The additional columns over [sys].[objects]
here are:
unique_index_id | This is the ID of the index that enforces the constraint. You can use [parent_object_id] and [unique_index_id] to reference the [object_id] and [index_id] columns in [sys].[indexes] . Note that this is particularly important, because looking at [sys].[indexes] , and, in turn, [sys].[index_columns] , tells us what columns are involved in the constraint. |
---|---|
is_system_named | Again, as for [sys].[default_constraints] , with the same caveats (see above). |
One thing that is uncovered here, through implication, is that functionally there is very little difference between a UNIQUE INDEX
, a PRIMARY KEY
and a UNIQUE
constraint. The only major differences are:
- A table can have only one
PRIMARY KEY
- A
PRIMARY KEY
does not accept NULL values
Apart from that, they are, to all intents and purposes, exactly the same. And ignore people who tell you that you can only create a foreign key to a primary key, because that's simply not true.
So, on to the last one, [sys].[foreign_keys]
. The additional columns over [sys].[objects]
are:
referenced_object_id | This is the ID of the object that the foreign key targets. Note that the ID of the object that is the source of the link is in the [parent_object_id] column. |
---|---|
key_index_id | This is the ID of the index on the referenced object that is targetted by the foreign key. This can be a UNIQUE INDEX , a PRIMARY KEY constraint or a UNIQUE constraint. You can use [referenced_object_id] and [key_index_id] to reference the [object_id] and [index_id] columns in [sys].[indexes] . |
is_disabled | As for [sys].[check_constraints] , this is 1 if the constraint has been disabled using ALTER TABLE ... NOCHECK CONSTRAINT . |
is_not_for_replication | Again, as for [sys].[check_constraints] , this is 1 if the constraint was created with the NOT FOR REPLICATION keyword. This effectively means that replicated databases do not enforce the constraint during DML operations. |
is_not_trusted | Third time lucky, as for [sys].[check_constraints] , this is 1 if the constraint hasn't been verified for all rows. This will be 1 if the constraint has been disabled for some time, or was created on an existing table with the WITH NOCHECK keyword. |
delete_referential_action | This is one of the referential actions taken when a parent row is deleted - the referential actions are defined below. |
delete_referential_action_desc | This is the description of the delete referential action. |
update_referential_action | This is one of the referential actions taken when a parent row is updated - the referential actions are defined below. |
update_referential_action_desc | This is the description of the update referential action. |
is_system_named | This column, by now, needs no introduction! See above. |
The referential actions are:
Keyword | Value | Description |
---|---|---|
NO ACTION | 0 | This means that if the delete or update operation would leave orphaned rows in the parent table, then the DML operation fails. |
CASCADE | 1 | This means that the delete or update is 'cascaded' from the referenced table to the parent table. A delete of a referenced row deletes the parent table rows, an update of a referenced row updates the key values in the parent table row to match. |
SET NULL | 2 | This means that the delete or update causes rows in the parent table to have their key values set to NULL when rows in the referenced table are updated or deleted. |
SET DEFAULT | 3 | This means that the delete or update causes rows in the parent table to have their key values set to their default values when rows in the referenced table are updated or deleted. |
So - does that tell us everything we need to know about foreign key constraints? No. We still need to find out which columns are involved in the key. Much the same as we could look this up via [sys].[indexes]
and [sys].[index_columns]
for the key constraints, we need some extra information matching each parent column to each referenced column. Enter [sys].[foreign_key_columns]
:
constraint_object_id | This is the ID of the foreign key object itself. |
---|---|
constraint_column_id | This is a slightly misleading column name - I personally feel [key_ordinal] would have been a better choice here. The value is used to order the column reference pairs, and will always be contiguous, starting at 1. |
parent_object_id | This is the ID of the parent table - duplicated from [sys].[foreign_keys] . |
parent_column_id | This is the ID of the column in the parent table - this can be used along with [parent_object_id] to look up the relevant column in [sys].[columns] . |
referenced_object_id | This is the ID of the referenced table - duplicated from [sys].[foreign_keys] . |
referenced_column_id | This is the ID of the column in the referenced table - this can be used along with [referenced_object_id] to look up the relevant column in [sys].[columns] . |
Now we know everything we need to know in order to be able to understand, work with, and re-create constraints from meta data.
Next time we'll take a look at SQL modules, and understand how they tie in with the meta data views.