Login or Sign Up to become a member!
LessThanDot Sit Logo

LessThanDot

Data Management

Less Than Dot is a community of passionate IT professionals and enthusiasts dedicated to sharing technical knowledge, experience, and assistance. Inside you will find reference materials, interesting technical discussions, and expert tips and commentary. Once you register for an account you will have immediate access to the forums and all past articles and commentaries.

LTD Social Sitings

Lessthandot twitter Lessthandot Linkedin Lessthandot friendfeed Lessthandot facebook Lessthandot rss

Note: Watch for social icons on posts by your favorite authors to follow their postings on these and other social sites.

Your profile

    Search

    XML Feeds

    Google Ads

    « SQL Server Data Type Conversion Poster is available for downloadSQL Advent 2012 Day 7: Lack of constraints »
    comments

    This is day eight of the SQL Advent 2012 series of blog posts. In yesterday's post SQL Advent 2012 Day 7: Lack of constraints we touched a little upon foreign key constraints but today we are going to take a closer look at foreign keys. The two things that we are going to cover are the fact that you don't need a primary key in order to define a foreign key relationship, SQL Server by default will not index foreign keys

    You don't need a primary key in order to have a foreign key

    Most people will define a foreign key relationship between the foreign key and a primary key. You don't have to have a primary key in order to have a foreign key, if you have a unique index or a unique constraint then those can be used as well. Let's take a look at what that looks like with some code examples

    A foreign key with a unique constraint instead of a primary key
    First create a table to which we will add a unique constraint after creation

    1. CREATE TABLE TestUniqueConstraint(id int)
    2. GO

    Add a unique constraint to the table

    1. ALTER TABLE TestUniqueConstraint ADD CONSTRAINT ix_unique UNIQUE (id)
    2. GO

    Insert a value of 1, this should succeed

    1. INSERT  TestUniqueConstraint VALUES(1)
    2. GO

    Insert a value of 1 again, this should fail

    1. INSERT  TestUniqueConstraint VALUES(1)
    2. GO

    Msg 2627, Level 14, State 1, Line 2
    Violation of UNIQUE KEY constraint 'ix_unique'. Cannot insert duplicate key in object 'dbo.TestUniqueConstraint'. The duplicate key value is (1).
    The statement has been terminated.

    Now that we verified that we can't have duplicates, it is time to create the table that will have the foreign key

    1. CREATE TABLE TestForeignConstraint(id int)
    2. GO

    Add the foreign key to the table

    1. ALTER TABLE dbo.TestForeignConstraint ADD CONSTRAINT
    2.     FK_TestForeignConstraint_TestUniqueConstraint FOREIGN KEY
    3.     (id) REFERENCES dbo.TestUniqueConstraint(id)

    Insert a value that exist in the table that is referenced by the foreign key constraint

    1. INSERT TestForeignConstraint  VALUES(1)
    2. INSERT TestForeignConstraint  VALUES(1)

    Insert a value that does not exist in the table that is referenced by the foreign key constraint

    1. INSERT TestForeignConstraint  VALUES(2)

    Msg 547, Level 16, State 0, Line 1
    The INSERT statement conflicted with the FOREIGN KEY constraint "FK_TestForeignConstraint_TestUniqueConstraint". The conflict occurred in database "tempdb", table "dbo.TestUniqueConstraint", column 'id'.
    The statement has been terminated.

    As you can see, you can't insert the value 2 since it doesn't exist in the TestUniqueConstraint table

    A foreign key with a unique index instead of a primary key
    This section will be similar to the previous section, the difference is that we will use a unique index instead of a unique constraint

    First create a table to which we will add a unique index after creation

    1. CREATE TABLE TestUniqueIndex(id int)
    2. GO

    Add the unique index

    1. CREATE UNIQUE NONCLUSTERED INDEX ix_unique ON TestUniqueIndex(id)
    2. GO

    Insert a value of 1, this should succeed

    1. INSERT  TestUniqueIndex VALUES(1)
    2. GO

    Insert a value of 1 again , this should now fail

    1. INSERT  TestUniqueIndex VALUES(1)
    2. GO

    Msg 2601, Level 14, State 1, Line 2
    Cannot insert duplicate key row in object 'dbo.TestUniqueIndex' with unique index 'ix_unique'. The duplicate key value is (1).
    The statement has been terminated.

    Now that we verified that we can't have duplicates, it is time to create the table that will have the foreign key

    1. CREATE TABLE TestForeignIndex(id int)
    2. GO

    Add the foreign key constraint

    1. ALTER TABLE dbo.TestForeignIndex ADD CONSTRAINT
    2.     FK_TestForeignIndex_TestUniqueIndex FOREIGN KEY
    3.     (id) REFERENCES dbo.TestUniqueIndex(id)

    Insert a value that exist in the table that is referenced by the foreign key constraint

    1. INSERT TestForeignIndex  VALUES(1)
    2. INSERT TestForeignIndex  VALUES(1)

    Insert a value that does not exist in the table that is referenced by the foreign key constraint

    1. INSERT TestForeignIndex  VALUES(2)

    Msg 547, Level 16, State 0, Line 1
    The INSERT statement conflicted with the FOREIGN KEY constraint "FK_TestForeignIndex_TestUniqueIndex". The conflict occurred in database "tempdb", table "dbo.TestUniqueIndex", column 'id'.
    The statement has been terminated.

    As you can see, you can't insert the value 2 since it doesn't exist in the TestUniqueIndex table

    As you have seen with the code example, you can have a foreign key constraint that will reference a unique index or a unique constraint in addition to be able to reference a primary key

    Foreign keys are not indexed by default

    When you create a primary key, SQL Server will by default make that a clustered index. When you create a foreign key, there is no index created

    Scroll up to where we added the unique constraint to the TestUniqueConstraint table, you will see this code

    1. ALTER TABLE TestUniqueConstraint ADD CONSTRAINT ix_unique UNIQUE (id)

    All we did was add the constraint, SQL Server added the index behind the scenes for us in order to help enforce uniqueness more efficiently

    Now run this query below

    1. SELECT OBJECT_NAME(object_id) as TableName,
    2. name as IndexName,
    3. type_desc as StorageType
    4. FROM sys.indexes
    5. WHERE OBJECT_NAME(object_id) IN('TestUniqueIndex','TestUniqueConstraint')
    6. AND name IS NOT NULL

    You will get these results

    TableName	        IndexName	StorageType
    ---------------------   -----------     --------------
    TestUniqueConstraint	ix_unique	NONCLUSTERED
    TestUniqueIndex	        ix_unique	NONCLUSTERED

    As you can see both tables have an index

    Now let's look at what the case is for the foreign key tables. Run the query below

    1. SELECT OBJECT_NAME(object_id) as TableName,
    2. name as IndexName,
    3. type_desc as StorageType
    4. FROM sys.indexes
    5. WHERE OBJECT_NAME(object_id) IN('TestForeignIndex','TestForeignConstraint')

    Here are the results for that query

    TableName	      IndexName	StorageType
    --------------------- --------- -------------
    TestForeignConstraint	NULL	HEAP
    TestForeignIndex	NULL	HEAP
    

    As you can see no indexes have been added to the tables. Should you add indexes? In order to answer that let's see what would happen if you did add indexes. Joins would perform faster since it can traverse the index instead of the whole table to find the matching join conditions. Updates and deletes will be faster as well since the index can be used to find the foreign keys rows to update or delete (remember this depends if you specified CASCADE or NO ACTION when you create the foreign key constraint)
    So to answer the question, yes, I think you should index the foreign key columns



    That is all for day eight of the SQL Advent 2012 series, come back tomorrow for the next one, you can also check out all the posts from last year here: SQL Advent 2011 Recap

    About the Author

    User bio imageDenis has been working with SQL Server since version 6.5. Although he worked as an ASP/JSP/ColdFusion developer before the dot com bust, he has been working exclusively as a database developer/architect since 2002. In addition to English, Denis is also fluent in Croatian and Dutch, but he can curse in many other languages and dialects (just ask the SQL optimizer) He lives in Princeton, NJ with his wife and three kids.
    Social SitingsTwitterFacebookLinkedInHomePageFlickrLTD RSS Feed
    1790 views
    InstapaperVote on HN

    1 comment

    Comment from: alzdba [Member] Email
    alzdba Nice highlights !
    Need it to be said foreign keys help for performance too ( if declared correct ) !

    ref: http://www.scarydba.com/2010/11/22/do-foreign-key-constraints-help-performance/
    12/11/12 @ 00:41

    Leave a comment


    Your email address will not be revealed on this site.

    To mislead the spambots.

    Your URL will be displayed.
    (Line breaks become <br />)
    (Name, email & website)
    (Allow users to contact you through a message form (your email will not be revealed.)