Before we jump directly into applying indexes and tuning queries, let’s take a look at the types of indexes that are available and when you might want to use them.
As you can see, there are several types of Indexes, but we will be focusing on Clustered and Non Clustered Indexes and the relationship they have with each other.
I like to think of Unique as an attribute of an index. Both Clustered and Non Clustered Indexes can be Unique. Unique indexes have some hidden benefits the query optimizer can use. For example, if you are performing a SELECT DISTINCT on a column that has a Unique Index, SQL Server knows the values are already distinct and doesn’t have to do a SORT operation to weed out records.Books Online even has Non Clustered Indexes with INCLUDE as a type of index, but to me, INCLUDE is really not a type of index, it is just a way for you to get more bang for your buck out of Non Clustered indexes.
Filtered Indexes where introduced in SQL Server 2008 and are a good way to create that are less expensive to maintain. Filtered Indexes are an extension of Non Clustered Indexes and allow you to create an index on a subset of data. Filtered Indexes basically allow you to add a WHERE clause to a Non Clustered Index.
Finally, Full-Text, Spatial, and XML indexes are more specialized indexes that are only used when called for in certain scenarios.
If you notice, they have increased the maximum indexes per table from 250 to 1000. This is Inclusive of the Clustered Index, meaning you can have 1 Clustered Index and 249 Non Clustered Indexes in 2005 and 999 Non Clustered Indexes in 2008.The increase has a lot to do with the ability to add Filtered Indexes in 2008. You can create several different indexes on the same column with different ranges in each. Although in most circumstances, I wouldn’t even want to come close to these numbers because indexes have to be maintained and can cause negative performance impacts.To help reduce these performance impacts you can only create an index with 16 columns or 900 bytes, whichever comes first.
Identity columns have all of the qualities that make a good Clustered Index. So, let’s take a look why.The Clustered Index is duplicated across all of your Non Clustered Indexes so you want to make it as efficient as possible.
Having a Narrow Index means less data will be duplicated and you will be able to fit more data per page. Meaning Less I/O to read through your Indexes. This also leads to smaller databases, backups, and index maintenance times.
So why does Unique matter? Clustered Indexes are not required to be unique, but if you add values to your clustered index that are not unique, SQL Server will add a 4 byte internal identifier called a uniquifier. This kind of goes against our keeping the index narrow thing.
You don’t want to change the values in your Clustered Index for a couple of reasons. First of all, since the values of your Clustered Index are contained in your Non Clustered Indexes, every time you update your Clustered Index, all of your Non Clustered Indexes have to be updated. Secondly, the pointers in the Clustered Index have to be maintained. If the Clustered Index Key changes, the record has to change locations causing fragmentation.
Finally, if the Clustered Index is increasing, data is always added to the end of the Index. This is good because if you insert records in the middle of your table, SQL Server has to make room for the records causing page splits and page splits lead to fragmented indexes.
That leaves the job of the Non Clustered Index to cover all of the other commonly used queries that aren’t covered by the Clustered Index and if they can’t completely cover the query, they can at least efficiently find the record and link back to the Clustered Index to get any other information the query may need.
USE AdventureWorksGO--CleanupIF EXISTS (SELECT name FROM sysindexes WHERE name = 'ix_PersonContact_LastName') DROP INDEX ix_PersonContact_LastName ON Person.Contact--SeekSELECT Phone, EmailAddressFROM Person.ContactWHERE ContactID = 42--ScanSELECT Phone, EmailAddressFROM Person.ContactWHERE LastName = 'Wruck'--**********Lookup*********************CREATE NONCLUSTERED INDEX ix_PersonContact_LastNameON Person.Contact (LastName)SELECT Phone, EmailAddressFROM Person.ContactWHERE LastName = 'Wruck'--***********Include (Covering)***********CREATE INDEX ix_PersonContact_LastNameON Person.Contact (LastName) INCLUDE(Phone, EmailAddress) WITH (DROP_EXISTING = ON)SELECT Phone, EmailAddressFROM Person.ContactWHERE LastName = 'Wruck'--***********Hint************************SELECT Phone, EmailAddressFROM Person.Contact WITH (INDEX(PK_Contact_ContactID))WHERE LastName = 'Wruck'SELECT Phone, EmailAddressFROM Person.ContactWHERE LastName = 'Wruck'--***************************************
Select * is like developers throwing ninja stars at your server.Select * basically means that your query will ALWAYS have to do a lookup on the Clustered Index unless you add every column in the table to the Non-Clustered Index (which is generally not a very good idea).
DMVs give you a better perspective of your entire systemExpose information about your server/databaseFor example, every time SQL Server thinks it needs an index, it is recorded or every time a index is used (scan, seek, lookup, update)Stats reset whenever SQL is restartedMay want to collect information to analyze over time
Always look to make sure you are not going to create overlapping or duplicate indexes.You may be able to alter an existing index by simply adding an extra column to the INCLUDE.Consolidate Indexes whenever where you can.
The Art ofIndexing http://www.flickr.com/photos/h-k-d/2837128711/ Ken Simmons