In databases, I often need foreign keys in my tables, because I’ll want to use them to select content out from my tables. However this can often result in either bad index utilization in the selection or making additional indexes based on the foreign key(s) and the content I need to select out.

This in turn can result in ending up with “many” indexes and sometimes many big indexes.

However a method can be to make smaller indexes and use an INNER JOIN to join into the table an extra time.

I’m going to show the pattern with a relative simple example to illustrate, because it is about the pattern more so then the specifics layout, content and size of the table.

It is just a pattern/technique to keep in mind and have in the SQL toolbox.

Suppose you have two tables of a similar pattern to this:

Tables for self join example

(click for larger size)

I’ve let the SQL Server create my clustered indexes based on the primary key, which means I have a clustered index over my primary key(s).

When having to look up rows in JoinOne based on the foreign key, it’ll often look like this:

SELECT *

FROM JoinOne T1

WHERE FKOne = <VALUE>

Because I’ve added no other indexes to the tables, I’ll get an Index Scan or Table Scan when running the above query, which – as we – know is not a good thing most of the time.

This usually leads to the creating of a second index which indexes over my foreign key.

However if the table is large, and if I’ll need to extract many/most of the columns (like in my example with SELECT *), it can mean I’ll have to make either a complete index or one with many included columns, just ordered by my foreign key first.

If I make a small index with my foreign key and my primary key second, I can use this to join into my table again, and then after that use my primary key.

This is an index to illustrate:

Index for self join example

(click for larger size)

Then I can make the following query:

SELECT T2.*

FROM JoinOne T1

INNER JOIN JoinOne T2 ON

T1.PKOne = T2.PKOne

AND T1.PKTwo = T2.PKTwo

AND T1.PKThree = T2.PKThree

WHERE T1.FKOne = <VALUE>

When looking at its execution plan, it’ll show two index seeks instead of my previous index scan. This way I have a small index as possible, but maintain seeks in my execution.

Of course there are some considerations one need to take with this pattern. Firstly – if the tables are “small”, then the scan in itself might be alright, or the overhead of keeping a complete index for the foreign key is neglible.

However the advantage of the pattern is that I can have smaller indexes on (very) large tables, which means less overhead when inserting/updating – but still have only index seeks in my selections execution plans.

It is a useful technique in my opinion – when used at the right times, which of course is on a case by case evaluation as always.