Av rating:
Total votes: 68
Total comments: 9


András Belokosztolszki
Foreign Keys and their States
22 June 2007

Foreign keys enforce referential integrity and in this way contribute to the database's consistency. However, sometimes it is necessary to disable them temporarily. I have seen some confusion as to how one re-enables these disabled constraints in such a way that the database's consistency remains unaffected. So, in this article, I will examine some rarely explored areas that concern foreign keys; in particular, I will look at disabled and un-trusted foreign keys.

Why would one ever disable a foreign key?

An example for the need to temporarily disable foreign keys is when one wants to load a large batch of consistent data into a set of tables that reference each other via foreign keys. It is easier to load data on a per table basis, and if the foreign keys are disabled, one can load the table data for each table. For example if there is a foreign key set up on a table referencingTable and a referencedTable, one should start loading data for the referencedTable first. But foreign key relationships can be complex, and sometimes even the order in which data rows are added to a single table is significant. And, if we have millions of rows of data, and we are confident that they are consistent, disabling foreign keys is the best option.

After the large data batch is loaded, the database should once again enforce its consistency rules, and the foreign keys should be restored to their original states.

I will use a simple two table example to demonstrate how to enable and disable foreign keys, and how to detect inconsistencies in the database. The two tables are tableReferenced and tableReferencing. As the name suggests the tableReferencing will reference the tableReferenced table.

To create and populate the two tables run:

CREATE TABLE tableReferenced 
   
colA INT NOT NULL PRIMARY KEY IDENTITY(11)
    , 
colB NVARCHAR(20DEFAULT N'some data'
    
)
GO

INSERT INTO tableReferenced DEFAULT VALUES
INSERT INTO 
tableReferenced DEFAULT VALUES
GO

CREATE TABLE tableReferencing
    
(
      
colC INT NOT NULL
               
PRIMARY KEY
               
IDENTITY(22),
      
colARef INT NOT NULL
           
CONSTRAINT FK_References 
                 
REFERENCES tableReferenced colA 
    )
GO

INSERT INTO tableReferencing VALUES  )
INSERT INTO tableReferencing VALUES  )
INSERT INTO tableReferencing VALUES  2 

GO

This will create the two tables and populates them like this:

The foreign key is established, and one can check this by inserting a row that would violate referential integrity:

INSERT INTO tableReferencing VALUES 1000 )

This insert should fail, because there is no corresponding row in tableReferenced. SQL Server, as expected, reacts with the following error message:

Msg 547, Level 16, State 0, Line 1

The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_References". The conflict occurred in database "FKplay",
table "dbo.tableReferenced", column 'colA
'.

The statement has been terminated.

To disable a foreign key in a SQL Server database, you can use a simple ALTER statement such as:

ALTER TABLE tableReferencing NOCHECK CONSTRAINT FK_References

Following this statement on can insert the following row that would violate the referential integrity:

INSERT INTO tableReferencing VALUES 1000 )

SQL Server confirms this, and the row has been inserted. No error messages were sent.

The data in our tables is now inconsistent, and looks like this

And this is the point where many DBAs get confused about how to re-enable the foreign key. The statement I have seen used most often looks like this:

ALTER TABLE tableReferencing CHECK CONSTRAINT FK_References

This seems to be the most obvious statement, since it just replaces the NOCHECK in the disabling statement with CHECK.

If we now try to insert a new row into the table that refers to a non-existent row, as follows:

INSERT INTO tableReferencing VALUES 1001 )

We get the expected error message, which would seem to indicate that the foreign key is working as expected.

However, if the data insertions that were performed during the period the foreign key was disabled have left the database table in an inconsistent state, we do not see an error immediately. In the above example, we have a row in the tableReferencing table that is referencing a row with id 1000 in the tableReferences table. This row does not exist. If we execute a select statement that joins the two tables, we still get results, e.g. the following query executes without any errors:

SELECT  *
FROM    tableReferencing
      
INNER JOIN tableReferenced ON tableReferencing.colARef tableReferenced.colA


It will not return anything for the violating row. However, other queries may be less lucky, and applications may not be ready to handle their results.

One can easily check if the table is consistent, and whether referential integrity is satisfied, by executing:

DBCC CHECKCONSTRAINTS ('tableReferencing')

In our case it returns:

Table Constraint Where
------------------------- ---------------- -------------------
[dbo].[tableReferencing] [FK_References] [colARef] = '1000' 
DBCC execution completed. If DBCC printed error messages,
contact your system administrator.

Indicating that there is a referential integrity violation.

At the moment we may think that the foreign key is back to the original state, i.e. the state that we had before we disabled it. However, this is not the case. The foreign key that we have now is in a so called "untrusted" state. You can see this by looking at the sys.foreign_keys system view on SQL Server 2005, or at the status column of the sysobjects table on SQL Server 2000.

If we wanted to create a new foreign key that was in exactly the same state as the current one, we would need to execute:

ALTER TABLE tableReferencing WITH NOCHECK
ADD CONSTRAINT 
FK_References FOREIGN KEY colARef 
   
REFERENCES dbo.tableReferenced colA )

Note the WITH NOCHECK part.

When we re-enabled the disabled foreign key, SQL Server did not perform checks to ensure that the tables involved in the foreign key relationship satisfied the constraints imposed by that foreign key. This made that operation fast, since SQL Server did not need to scan the relevant tables, but this was at the expense of database integrity.

To change the foreign key back to its original state, we need to let SQL Server check the validity of the constraint. To do this, the statement we should have used is:

ALTER TABLE tableReferencing WITH CHECK CHECK CONSTRAINT FK_References

This statement will make SQL Server check the data, and in our case it will find that the data in the tables is inconsistent with the foreign key constraint. On fixing these issues (in this case just dropping the violating row from the table tableReferencing), the state of the foreign key is restored to its original state. This will flip back the is_trusted flag in sys.foreign_keys, as well as enabling the constraint.

So to see which foreign keys are enabled or disabled, you can check the sys.foreign_keys table on 2005. It has a is_disabled column that has the value of 1 if the foreign key is disabled. Similarly you can get the information about whether or not a constraint is trusted.

Summary

When you disable a foreign key, not only will SQL Server disable it, it will, in addition, flip its state to non-trusted. When you enable it with the CHECK keyword, the trustedness of the foreign key is not restored. You can alter a foreign key's state using an alter statement on the table. The CHECK and NOCHECK parts control whether the foreign key is enabled or disabled. The WITH CHECK and WITH NOCHECK options control whether SQL Server should check the consistency of the tables involved, and this way establish whether the foreign key is trusted or not. You can also use DBCC CHECKCONSTRAINTS to check whether the referential integrity constraint is satisfied on a particular table.



This article has been viewed 16213 times.
András Belokosztolszki

Author profile: András Belokosztolszki

András is a software architect/project manager at Red Gate Software and is the architect of SQL Compare versions 4, 5 and 6, SQL Refactor and SQL Log Rescue. He has a PhD in database technologies from Cambridge University and is fluent in at least four languages, including Hungarian, English, Russian and German.

Search for other articles by András Belokosztolszki

Rate this article:   Avg rating: from a total of 68 votes.


Poor

OK

Good

Great

Must read
 
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: Gratitude
Posted by: Bozidar (not signed in)
Posted on: Friday, June 29, 2007 at 1:33 AM
Message: Excelent extent of DBA's knowledge!!!
Thank You András!!!

Sincerely,
Bozidar

Subject: DB
Posted by: Anonymous (not signed in)
Posted on: Friday, June 29, 2007 at 6:44 AM
Message: That was great

Subject: Interesting but not super
Posted by: Anonymous (not signed in)
Posted on: Friday, June 29, 2007 at 9:01 AM
Message: Interesting article but nothing more.
One interesting moment about
"DBCC checkconstraints" any other information can simply fouunded in BOL.
Anyway, thanks for article! Writing more the same articles pls! :o)

Subject: Good for new people
Posted by: Anonymous (not signed in)
Posted on: Friday, June 29, 2007 at 11:46 AM
Message: Interesting article and good for the people who has just started working on Relational databases.

Subject: Good and clear BUT status column value would be nice to know...
Posted by: SAinCA (view profile)
Posted on: Friday, June 29, 2007 at 3:56 PM
Message: What's the status code value to look for in SS2000 where you said "The foreign key that we have now is in a so called 'untrusted' state. You can see this by looking at the ... status column of the sysobjects table on SQL Server 2000", please? I have unintelligible numbers (2, 2050, 4098, 6146, 12290 and 14338) and, contrary to prior posts, BOL doesn't elucidate...

Subject: Re: ...status column value would be nice to know....
Posted by: The SQL Server Thought Police (view profile)
Posted on: Monday, July 02, 2007 at 3:44 AM
Message: The meaning of the individual status bits can be changed from one release of SQL Server to another. Microsoft are not obliged to maintain their meaning. In normal use, therefore, it is much better to use ObjectProperty() to examine the state of a constraint. This will give you a much more portable and understandable code. This is also very well documented on BOL http://msdn2.microsoft.com/en-us/library/aa276849(SQL.80).aspx

Subject: great
Posted by: Arun (not signed in)
Posted on: Tuesday, July 03, 2007 at 11:08 PM
Message: very informative. thanks a lot

Subject: great
Posted by: Arun (not signed in)
Posted on: Tuesday, July 03, 2007 at 11:10 PM
Message: very informative. thanks a lot

Subject: sp_msforeachtable can be used in conjunction with:
Posted by: Anonymous (not signed in)
Posted on: Friday, July 06, 2007 at 8:56 AM
Message: When performing data loads across many tables, the undocumented sp_msforeachtable table comes in handy. For example:

sp_msforeachtable "dbcc checktable ('?')"

http://codebetter.com/blogs/raymond.lewallen/archive/2005/03/25/60596.aspx



 









Phil Factor
Bunnikins!
 When an IT manager is selected as a victim of office politics of a large corporate, it is time for him to engage in... Read more...



 View the blog
On the Trail of the Expanding Databases
 It is sometimes difficult for other IT people to understand the constraints that DBAs have to work... Read more...

Using Powershell to Generate Table-Creation Scripts
 For all of us who learn best by trying out examples, Bob Sheldon produces a PowerShell script file for... Read more...

SQL Toolbelt 2008: Predominantly an Engineering Task
 The conversion of the Red-Gate tools to be compatible with SQL Server 2008 might not seem, on first... Read more...

Audit Crosschecks
 In this short article, the second of a 2-part series, William suggests a solution, using SQL Data... Read more...

XML Jumpstart Workbench
 In which Robyn and Phil decide that the best way of starting to learn XML is to jump in and take a ride... Read more...

Beginning SQL Server 2005 Reporting Services Part 1
 Steve Joubert begins an in-depth tour of SQL Server 2005 Reporting Services with a step-by-step guide... Read more...

Ten Common Database Design Mistakes
 Database design and implementation is the cornerstone of any data centric project (read 99.9% of... Read more...

SQL Server Full Text Search Language Features
 SQL Full-text Search (SQL FTS) is an optional component of SQL Server 7 and later, which allows fast... Read more...

Beginning SQL Server 2005 Reporting Services Part 2
 Continuing his in-depth tour of SQL Server 2005 Reporting Services, Steve Joubert demonstrates the most... Read more...

Executing SSIS Packages
 Nigel Rivett demonstrates how to execute all SSIS packages in a given folder using either an SSIS... Read more...

Over 150,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.

Join Simple Talk