A Foreign Key is a constraint. Due to this definition, it only places a limitation on what data can be inserted into a column(s) and does not address the indexes needed to help performance when joining tables

Take the following design.

foreignkeysandindexes-1

If you wanted to join the 2 table to return the person’s name and address you would write the following query:

[php] Select Concat(p.FirstName ,’ ‘ ,p.LastName) ,a.StreetName ,a.City from person p inner join address a on (p.PersonID = a.PersonID) where p.personID in (100,1000,2000) [/php]

The current design takes into account the primary cluster indexes on the person. personID column, but does not have an index defined on the Address.personID column.

The foreign keys are normally used in the From clause for your joins on how to link the data between the 2 tables. It is highly recommended to create indexes on both tables for these columns. Most of the time (but not always), the parent table will already have an index defined on the column used in the foreign key definition. In the example above, most likely there is a cluster primary key defined on the personID column in the person table. But unless it was specified, there would not be a non-clustered index on the personID in the address table.

When executing the query above, SQL Server generates the following execution plan. As you can see, SQL Server is hinting an index would help improve query performance. With the limitations of just a couple of personID from the person table, SQL Server is using an index Seek to find the data needed. It is using the clustered index on the address table but you can see it is still having to run a scan of the index. This means if your table has 3 million row, it would have to scan all 3 million to pull back just the records that match the limitation.

foreignkeysandindexes-2

Now if you add your nonclustered index on the Foreign Key column in the address table and re-execute the statement, SQL Server is now using Index Seeks for both tables.

[php]create index FKIndexpersonID on dbo.address(personID)[/php] foreignkeysandindexes-3

In reviewing the above query plan, I can see there is one more step I could take to help performance. The key lookup on the clustered PK index is due to query wanting data which is not a part of the index used in the join (streetName, city)

The original execution plan was hinting SQL Server would benefit from a covering index to improve the performance. If I drop and recreate the index with the two include columns, the execution plan is now showing SQL server only needed to use 2 index seeks to pull the data needed for the results of the query.

[php] drop index FKIndexpersonID on dbo.address(personID) [/php] foreignkeysandindexes-4

Defining a foreign key constraint does not create the underlying index. A constraint only put a limit on what data can be inserted into the column.

Adding a non-clustered index to the Foreign Key Column in the child table can increase query performance by removing Table or Index Scans with Index Seeks. Adding include columns to make the index a covering index can improve performance even further by removing the Key Look to retrieve the extra columns from the child table.

Share This