Why use the INCLUDE clause when creating an index?

248,666

Solution 1

If the column is not in the WHERE/JOIN/GROUP BY/ORDER BY, but only in the column list in the SELECT clause is where you use INCLUDE.

The INCLUDE clause adds the data at the lowest/leaf level, rather than in the index tree. This makes the index smaller because it's not part of the tree

INCLUDE columns are not key columns in the index, so they are not ordered. This means it isn't really useful for predicates, sorting etc as I mentioned above. However, it may be useful if you have a residual lookup in a few rows from the key column(s)

Another MSDN article with a worked example

Solution 2

You would use the INCLUDE to add one or more columns to the leaf level of a non-clustered index, if by doing so, you can "cover" your queries.

Imagine you need to query for an employee's ID, department ID, and lastname.

SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5

If you happen to have a non-clustered index on (EmployeeID, DepartmentID), once you find the employees for a given department, you now have to do "bookmark lookup" to get the actual full employee record, just to get the lastname column. That can get pretty expensive in terms of performance, if you find a lot of employees.

If you had included that lastname in your index:

CREATE NONCLUSTERED INDEX NC_EmpDep 
  ON Employee(EmployeeID, DepartmentID)
  INCLUDE (Lastname)

then all the information you need is available in the leaf level of the non-clustered index. Just by seeking in the non-clustered index and finding your employees for a given department, you have all the necessary information, and the bookmark lookup for each employee found in the index is no longer necessary --> you save a lot of time.

Obviously, you cannot include every column in every non-clustered index - but if you do have queries which are missing just one or two columns to be "covered" (and that get used a lot), it can be very helpful to INCLUDE those into a suitable non-clustered index.

Solution 3

This discussion is missing out on the important point: The question is not if the "non-key-columns" are better to include as index-columns or as included-columns.

The question is how expensive it is to use the include-mechanism to include columns that are not really needed in index? (typically not part of where-clauses, but often included in selects). So your dilemma is always:

  1. Use index on id1, id2 ... idN alone or
  2. Use index on id1, id2 ... idN plus include col1, col2 ... colN

Where: id1, id2 ... idN are columns often used in restrictions and col1, col2 ... colN are columns often selected, but typically not used in restrictions

(The option to include all of these columns as part of the index-key is just always silly (unless they are also used in restrictions) - cause it would always be more expensive to maintain since the index must be updated and sorted even when the "keys" have not changed).

So use option 1 or 2?

Answer: If your table is rarely updated - mostly inserted into/deleted from - then it is relatively inexpensive to use the include-mechanism to include some "hot columns" (that are often used in selects - but not often used on restrictions) since inserts/deletes require the index to be updated/sorted anyway and thus little extra overhead is associated with storing off a few extra columns while already updating the index. The overhead is the extra memory and CPU used to store redundant info on the index.

If the columns you consider to add as included-columns are often updated (without the index-key-columns being updated) - or - if it is so many of them that the index becomes close to a copy of your table - use option 1 I'd suggest! Also if adding certain include-column(s) turns out to make no performance-difference - you might want to skip the idea of adding them:) Verify that they are useful!

The average number of rows per same values in keys (id1, id2 ... idN) can be of some importance as well.

Notice that if a column - that is added as an included-column of index - is used in the restriction: As long as the index as such can be used (based on restriction against index-key-columns) - then SQL Server is matching the column-restriction against the index (leaf-node-values) instead of going the expensive way around the table itself.

Solution 4

Basic index columns are sorted, but included columns are not sorted. This saves resources in maintaining the index, while still making it possible to provide the data in the included columns to cover a query. So, if you want to cover queries, you can put the search criteria to locate rows into the sorted columns of the index, but then "include" additional, unsorted columns with non-search data. It definitely helps with reducing the amount of sorting and fragmentation in index maintenance.

Solution 5

One reason to prefer INCLUDE over key-columns if you don't need that column in the key is documentation. That makes evolving indexes much more easy in the future.

Considering your example:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

That index is best if your query looks like this:

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...

Of course you should not put columns in INCLUDE if you can get an additional benefit from having them in the key part. Both of the following queries would actually prefer the col2 column in the key of the index.

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...
   AND col2 = ...
SELECT TOP 1 col2, col3
  FROM MyTable
 WHERE col1 = ...
 ORDER BY col2

Let's assume this is not the case and we have col2 in the INCLUDE clause because there is just no benefit of having it in the tree part of the index.

Fast forward some years.

You need to tune this query:

SELECT TOP 1 col2
  FROM MyTable
 WHERE col1 = ...
 ORDER BY another_col

To optimize that query, the following index would be great:

CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2)

If you check what indexes you have on that table already, your previous index might still be there:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

Now you know that Col2 and Col3 are not part of the index tree and are thus not used to narrow the read index range nor for ordering the rows. Is is rather safe to add another_column to the end of the key-part of the index (after col1). There is little risk to break anything:

DROP INDEX idx1 ON MyTable;
CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2, Col3);

That index will become bigger, which still has some risks, but it is generally better to extend existing indexes compared to introducing new ones.

If you would have an index without INCLUDE, you could not know what queries you would break by adding another_col right after Col1.

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

What happens if you add another_col between Col1 and Col2? Will other queries suffer?

There are other "benefits" of INCLUDE vs. key columns if you add those columns just to avoid fetching them from the table. However, I consider the documentation aspect the most important one.

To answer your question:

what guidelines would you suggest in determining whether to create a covering index with or without the INCLUDE clause?

If you add a column to the index for the sole purpose to have that column available in the index without visiting the table, put it into the INCLUDE clause.

If adding the column to the index key brings additional benefits (e.g. for order by or because it can narrow the read index range) add it to the key.

You can read a longer discussion about this here:

https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes

Share:
248,666
Cory
Author by

Cory

Updated on November 20, 2020

Comments

  • Cory
    Cory over 3 years

    While studying for the 70-433 exam I noticed you can create a covering index in one of the following two ways.

    CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)
    

    -- OR --

    CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)
    

    The INCLUDE clause is new to me. Why would you use it and what guidelines would you suggest in determining whether to create a covering index with or without the INCLUDE clause?

  • ZygD
    ZygD almost 13 years
    Are you sure you'd use this index? Why EmployeeID? You only need DepartmentID in the key columns? You have been quoted here as authoratitive: stackoverflow.com/q/6187904/27535
  • JNK
    JNK over 12 years
    Your explanation is good but doesn't actually line up with the use case that you outline. The key column(s) should be on the filter or JOIN keys in the query, and the INCLUDEs need to be the data you are retrieving but not sorting.
  • JMarsch
    JMarsch over 11 years
    So then, this would be a technique for creating a less expensive version of a covered index?
  • Asken
    Asken over 11 years
    is there a way to prove that it is actually using less memory? it's what i'd expect too but i'm getting some static about this at work
  • mrdenny
    mrdenny over 11 years
    Given that you have to load the page from the heap or clustered index into memory as well as the index page which means that you are putting duplicate data into memory the math becomes pretty simple. As for a way to specifically measure it, no there's not.
  • AnandPhadke
    AnandPhadke about 11 years
    First of all the index Employee(EmployeeID, DepartmentID) will not be used to filter DepartmentID = 5. Because its order is not matching
  • Tola Odejayi
    Tola Odejayi about 11 years
    @gbn, would you mind explaining this sentence in more detail, and explain why it means that the include clause is not useful for sorting, etc: "The INCLUDE clause adds the data at the lowest/leaf level, rather than in the index tree. This makes the index smaller because it's not part of the tree"
  • ZygD
    ZygD about 11 years
    @JMarsch: sorry for the late reply, but yes, this is exactly what it is.
  • ZygD
    ZygD about 11 years
    @Tola Odejayi: INCLUDE columns are not key columns in the index, so they are not ordered. This makes them not typically useful for JOINs or sorting. And because they are not key columns, they don't sit in the whole B-tree structure like key columns
  • ZygD
    ZygD about 11 years
    @TolaOdejayi: and read this series too (link to just one bit only that is relevant for this answer) practicalsqldba.com/2013/03/…
  • Chisko
    Chisko about 7 years
    While this is the most accepted answer, I think further explanation is needed, what if for some queries the column is part of the SELECT and for some not?\
  • Hink
    Hink over 5 years
    If select uses only key and included columns (in select-list or in where-clause), then you can see only one search step from this index in execution plan. If select uses more columns, then optimizer must schedule additional search from physical row using clustered index to get extra values. Included columns should be used carefully, because such index contains duplicate data that need extra space and must be updated when non-key values are changed. Dont use it, if your commands are like "select * from ...".
  • Sam
    Sam almost 2 years
    I am using a column that heavily used. When I do "INCLUDE" it speeds up my queries for more than twice (yes, simply used INCLUDE). However, the column is also being updated regularly. Am I doing a wrong thing?