Click here to monitor SSC
SQLServerCentral is supported by Red Gate Software Ltd.
 
Log in  ::  Register  ::  Not logged in
 
 
 

Foreign Keys

By Ravi Lobo,

If you don’t have a key, you don’t have a table!
                                          - Joe Celko

Foreign Keys

For a long time I didn't gave much importance to foreign keys (FK). Occasionally they were a pain while deleting or inserting ,some times while truncating. Mostly they were harmless and their presence was ignored. Some time back for no apparent reason I developed a genuine interest into them and started collecting what ever possible information.

This is not a know-it-all article, but a collection of loosely coupled scripts organized at a single place. I am sure that there is more to it and expecting some of that in the feedback.

One of the most important assets of an organization is the DATA and if it is not proper the result will be chaos. Having data in a CHILD table without a reference to MASTER is unimaginable. FK's will be handy here.

Terminology

MASTER TABLE - Table having Primary Key (PK)
CHILD TABLE - Table having Foreign Key (FK)

Since the objective is to understand the concept,I have used thin tables with simple identifier names that may not satisfy any standard nomenclature!

Where does FK's fit?

SQL Server 2000 supports 5 types of constraints, NOT NULL, CHECK, UNIQUE, PRIMARY KEY and FOREIGN KEY. FK constraints identify the relationships between tables. A foreign key in one table points to a candidate key (PK) in another table. Foreign keys prevent actions that would leave rows with foreign key values when there are no candidate keys with that value.

Quick Check

Execute the following SQL in your production DB,
SELECT count(*) FROM sysreferences
If the o/p is zero or near to it, then you may want to consider creating some FK's!

Meta Data

Other than sysobjects most of the information on reference keys is stored in following three system tables,

  • sysreferences
  • sysconstraints
  • sysforeignkeys

Archipelago

Without FKs, tables will be similar to a group of islands. Following script will identify these tables,


SELECT name [Tables NOT having FK]
  FROM sysobjects 
 WHERE xtype='U' 
   AND objectproperty(object_id(name),'TableHasForeignKey')= 0
 ORDER BY 1
 

Creating a FK (Column level)

I am including this section only for the sake of completion. Hence not much on the topic!


CREATE TABLE master(pkey int PRIMARY KEY, data varchar(10))
GO
CREATE TABLE child (fkey int CONSTRAINT fk_master_child 
                             FOREIGN KEY (fkey) REFERENCES master(pkey))
Note: If you ignore CONSTRAINT fk_master_child from the above code SQL Server will create FK
with its own name. The name may not be meaningful!

Creating a FK (Table level)

In case the FK is a COMPOSITE KEY then it can not be defined on column level. A COMPOSITE KEY is the one which has more than one columns in it. This key uniquely identifies records in the MASTER table. Composite keys need to be defined on table level.


CREATE TABLE master(
 pkey1 int 
,pkey2 int                     
,data varchar(10)
,CONSTRAINT pk_master PRIMARY KEY (pkey1,pkey2))
GO
CREATE TABLE child (
 fkey1 int 
,fkey2 int
,CONSTRAINT fk_master_child FOREIGN KEY (fkey1,fkey2) REFERENCES master(pkey1,pkey2))
GO

ON DELETE CASCADE

Once FK is set, you can NOT delete records from MASTER, if a CHILD table is referring the same records by a FK. This feature can be overridden using ON DELETE CASCADE. This option deletes respective records from CHILD, when DELETE is executed on the MASTER.


CREATE TABLE master(pkey int PRIMARY KEY, data varchar(10))
GO
CREATE TABLE child (fkey int CONSTRAINT fk_master_child 
                             FOREIGN KEY (fkey) 
                             REFERENCES master(pkey) ON DELETE CASCADE )
Go
INSERT master 
SELECT 1, 'a'
GO
INSERT child
SELECT 1
GO
DELETE master 
 WHERE pkey = 1
GO
SELECT * FROM child 

Note: CASCADE can also be used for UPDATE operations. ON DELETE CASCADE can be replaced by the default option, ON DELETE NO ACTION which will raise an error on FK violation.

FK referencing PK on a same table

A FK can refer to a PK on the same table as below.


CREATE TABLE master(
 emp_id   int PRIMARY KEY
,emp_name varchar(10)
,mgr_id   int CONSTRAINT fk_master FOREIGN KEY (mgr_id) REFERENCES master(emp_id)
)
Note: An employee/Manager scenario is an example for the above case.

FK is PK


CREATE TABLE master(
 pkey int PRIMARY KEY
,data varchar(10))

CREATE TABLE child(
 fkey int PRIMARY KEY FOREIGN KEY (fkey) REFERENCES master(pkey))

The PK of CHILD table is also a FK to MASTER table. Since there is a PK on the CHILD table it can not have duplicate records Hence for each record in MASTER there will be maximum 1 or 0 records in CHILD.

In case the number of records in both the tables are same, that means there is an 1:1 relationship between the tables. Hence there is non need for CHILD table, the columns in the CHILD can be moved to MASTER.

Recursive

I can not imagine any practical use of below code, but theoretically it is possible.


CREATE TABLE master(
 pkey int PRIMARY KEY FOREIGN KEY (pkey) REFERENCES master(pkey))

Dependants

sp_fkeys system SP lists the CHILD tables for a given MASTER table.


sp_fkeys 'table_name'
GO
Following code list all the tables participating in a relationship.

SELECT object_name(fkeyid) AS child
      ,object_name(constid)AS FK
      ,object_name(rkeyid) AS master
 FROM sysreferences 
ORDER BY 1
Similar to above code following script list the columns participating in a relationship.

SELECT object_name(rkeyid) Master_Table
      ,sc2.name Master_Cols
      ,object_name(fkeyid) Child_Table 
      ,sc1.name Child_Cols
      ,sf.keyno Col_Order
 FROM sysforeignkeys  sf
INNER JOIN syscolumns sc1 ON sf.fkeyid = sc1.id AND sf.fkey = sc1.colid
INNER JOIN syscolumns sc2 ON sf.rkeyid = sc2.id AND sf.rkey = sc2.colid
ORDER BY rkeyid,fkeyid,keyno

Trees and Forests

Tree view will give a hierarchical representation, which will be easier to understand.

For e.g.

World
   North America
      Canada
      United States
         Washington
            Redmond
         New York
            New York City
   Europe
      France
         Paris
The following code gives a tree view for a parent table. The code is from BOL, I have done miner changes to fulfill our requirement. Search for "Expanding Hierarchies" in BOL for the actual code

CREATE PROCEDURE expand (@current char(20)) as
SET NOCOUNT ON
DECLARE @level int, @line char(20)
CREATE TABLE #stack (item char(20), level int)
INSERT INTO #stack VALUES (@current, 1)
SELECT @level = 1

WHILE @level > 0
BEGIN
   IF EXISTS (SELECT * FROM #stack WHERE level = @level)
      BEGIN
         SELECT @current = item
         FROM #stack
         WHERE level = @level
         SELECT @line = space(@level - 1) + @current
         PRINT @line
         DELETE FROM #stack
         WHERE level = @level
            AND item = @current
         INSERT #stack
            SELECT object_name(fkeyid), @level + 1
            FROM sysreferences
            WHERE object_name(rkeyid) = @current
         IF @@ROWCOUNT > 0
            SELECT @level = @level + 1
      END
   ELSE
      SELECT @level = @level - 1
END -- WHILE

Lets test the SP,

CREATE TABLE grand_parent(id int PRIMARY KEY )
GO
CREATE TABLE parent      (id int PRIMARY KEY  FOREIGN KEY  (id) REFERENCES grand_parent(id))
GO
CREATE TABLE child       (id int PRIMARY KEY  FOREIGN KEY  (id) REFERENCES parent(id))
GO
e.g. expand 'grand_parent'

	grand_parent        
	   parent             
	     child             

Enable/Disable FK

Following code can be used to disable a FK,


ALTER TABLE <table_name> NOCHECK CONSTRAINT <constraint_name>
There may be a case when you want to disable all the FKs in a DB temporarily, Mostly while massaging the data on a staging server. The following code will do exactly that,

SET QUOTED_IDENTIFIER OFF
GO
-- Disable
EXECUTE sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT ALL"
GO
-- Enable
EXECUTE sp_msforeachtable "ALTER TABLE ? WITH CHECK CHECK CONSTRAINT ALL"
GO

Along with the FK's the above code will disable other constraints also. Be careful if you don't want to do that!
Note: You can not modify the definition of a column which is participating in a relationship even if FK constraint is disabled.

Trivia

All the following SQLs return 0 OR 1 depending on the property existing in the DB.

-- To check whether the FK is enabled or NOT?


SELECT OBJECTPROPERTY(object_id('myforeignkey'), 'CnstIsDisabled')
-- To check whether the Table has FK?

SELECT OBJECTPROPERTY ( object_id('eRisk_UserRole'),'TableHasForeignKey') 
-- To check whether the Table has PK?

SELECT OBJECTPROPERTY ( object_id('eRisk_UserRole'),'TableHasPrimaryKey') 

TRUNCATE

You can NOT TRUNCATE a MASTER table even though the CHILD table doesn't have any referencing record.


CREATE TABLE master(
 pkey int PRIMARY KEY
,data varchar(10))
GO 
CREATE TABLE child(
 fkey int FOREIGN KEY (fkey) REFERENCES master(pkey))
Even though both the table are empty, truncating the MASTER table will raise an error.

TRUNCATE TABLE master
Server: Msg 4712, Level 16, State 1, Line 1 
Cannot truncate table 'master' because it is being referenced by a FOREIGN KEY constraint. 

Conclusion

Recently I came across a highly transactional DB with no FK's! The data integrity was managed by the front-end application. This is to save the time which DB server needs to check for the constraints. I don't think the time saved is really worth it!

Some time back I was involved in a migration project where the source DB didn't had any FK's but the destination DB was full of them. I had a tough time clearing the orphaned records. Finally now we have a clean DB, that's what matters in the long run!

Total article views: 18862 | Views in the last 30 days: 17
 
Related Articles
SCRIPT

Get all the Child Tables up to N level

Get all the dependent tables for a master table up to N level(Till the leaf ) you can either find d...

FORUM

Problem with creating a Foreign Key

Problem with creating a Foreign Key

FORUM

Design of Master / Child Tables ???

What is the best way to design a Master Child tables with normalization

SCRIPT

Find Foreign Keys

This script lists all foreign keys with parent and child table information

FORUM

copy parent records with child records

copy master/child records in same table

Tags
advanced    
database design    
 
Contribute

Join the most active online SQL Server Community

SQL knowledge, delivered daily, free:

Email address:  

You make SSC a better place

As a member of SQLServerCentral, you get free access to loads of fresh content: thousands of articles and SQL scripts, a library of free eBooks, a weekly database news roundup, a great Q & A platform… And it’s our huge, buzzing community of SQL Server Professionals that makes it such a success.

Join us!

Steve Jones
Editor, SQLServerCentral.com

Already a member? Jump in:

Email address:   Password:   Remember me: Forgotten your password?
Steve Jones