¿Por qué usar la cláusula INCLUDE al crear un índice?

432

Mientras estudiaba para el examen 70-433, noté que puede crear un índice de cobertura de una de las dos formas siguientes.

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

- O -

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

La cláusula INCLUDE es nueva para mí. ¿Por qué lo usaría y qué pautas sugeriría para determinar si crear un índice de cobertura con o sin la cláusula INCLUDE?

Cory
fuente

Respuestas:

364

Si la columna no está en WHERE/JOIN/GROUP BY/ORDER BY, sino solo en la lista de columnas de la SELECTcláusula.

La INCLUDEcláusula agrega los datos en el nivel más bajo / hoja, en lugar de en el árbol de índice. Esto hace que el índice sea más pequeño porque no es parte del árbol.

INCLUDE columnsno son columnas clave en el índice, por lo que no están ordenadas. Esto significa que no es realmente útil para predicados, clasificación, etc. como mencioné anteriormente. Sin embargo, puede ser útil si tiene una búsqueda residual en algunas filas de las columnas clave

Otro artículo de MSDN con un ejemplo trabajado

gbn
fuente
77
Entonces, ¿sería una técnica para crear una versión menos costosa de un índice cubierto?
JMarsch
3
@gbn, ¿le importaría explicar esta oración con más detalle y explicar por qué significa que la cláusula de inclusión no es útil para la clasificación, etc.: "La cláusula INCLUDE agrega los datos en el nivel más bajo / hoja, en lugar de en el árbol de índice . Esto hace que el índice sea más pequeño porque no es parte del árbol "
Tola Odejayi
44
@JMarsch: perdón por la respuesta tardía, pero sí, esto es exactamente lo que es.
Gbn
10
@Tola Odejayi: las columnas INCLUDE no son columnas clave en el índice, por lo que no están ordenadas. Esto hace que no sean típicamente útil para las uniones o clasificación. Y porque son columnas no clave, no se sienten en toda la estructura de árbol B como columnas de clave
GBN
44
Si bien esta es la respuesta más aceptada, creo que se necesita más explicación, ¿y si en algunas consultas de la columna es parte de la SELECTy para algunos no \?
Chisko
215

Usaría INCLUDE para agregar una o más columnas al nivel de hoja de un índice no agrupado, si al hacerlo, puede "cubrir" sus consultas.

Imagine que necesita consultar la identificación de un empleado, la identificación del departamento y el apellido.

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

Si tiene un índice no agrupado en (Id. De empleado, Id. De departamento), una vez que encuentre a los empleados de un departamento determinado, ahora tiene que hacer una "búsqueda de marcadores" para obtener el registro completo de los empleados, solo para obtener la columna de apellido . Eso puede ser bastante costoso en términos de rendimiento, si encuentra muchos empleados.

Si ha incluido ese apellido en su índice:

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

entonces toda la información que necesita está disponible en el nivel de hoja del índice no agrupado. Simplemente buscando en el índice no agrupado y encontrando a sus empleados para un departamento determinado, tiene toda la información necesaria, y la búsqueda de marcadores para cada empleado encontrado en el índice ya no es necesaria -> ahorra mucho tiempo.

Obviamente, no puede incluir todas las columnas en cada índice no agrupado, pero si tiene consultas que faltan solo una o dos columnas para "cubrir" (y que se usan mucho), puede ser muy útil INCLUIR esas en un índice no agrupado adecuado.

marc_s
fuente
25
¿Estás seguro de que usarías este índice? ¿Por qué EmployeeID? ¿Solo necesita DepartmentID en las columnas clave? Usted ha sido citado aquí como autoritario: stackoverflow.com/q/6187904/27535
gbn
3
Su explicación es buena, pero en realidad no se alinea con el caso de uso que describe. La (s) columna (s) clave (s) deben estar en el filtro o las JOINclaves en la consulta, y las INCLUDEs deben ser los datos que está recuperando pero no ordenando.
JNK
15
En primer lugar, el índice Empleado (
Id
29

Esta discusión se está perdiendo en el punto importante: la pregunta no es si las "columnas sin clave" son mejores para incluir como columnas de índice o como columnas incluidas .

La pregunta es ¿qué tan costoso es usar el mecanismo de inclusión para incluir columnas que realmente no son necesarias en el índice ? (normalmente no forma parte de las cláusulas where, pero a menudo se incluye en las selecciones). Entonces tu dilema es siempre:

  1. Utilice el índice en id1, id2 ... idN solo o
  2. Usar índice en id1, id2 ... idN más incluir col1, col2 ... colN

Donde: id1, id2 ... idN son columnas que se usan a menudo en restricciones y col1, col2 ... colN son columnas que se seleccionan a menudo, pero generalmente no se usan en restricciones

(La opción de incluir todas estas columnas como parte de la clave de índice siempre es una tontería (a menos que también se usen en restricciones), porque siempre sería más costoso de mantener ya que el índice debe actualizarse y ordenarse incluso cuando "llaves" no han cambiado).

¿Entonces usa la opción 1 o 2?

Respuesta: Si su tabla rara vez se actualiza, en su mayoría se inserta o se elimina, entonces es relativamente económico usar el mecanismo de inclusión para incluir algunas "columnas activas" (que a menudo se usan en selecciones, pero no se usan a menudo en restricciones) ya que las inserciones / eliminaciones requieren que el índice se actualice / clasifique de todos modos y, por lo tanto, se asocia una pequeña sobrecarga adicional con el almacenamiento de algunas columnas adicionales mientras ya se actualiza el índice. La sobrecarga es la memoria adicional y la CPU utilizada para almacenar información redundante en el índice.

Si las columnas que considera agregar como columnas incluidas a menudo se actualizan (sin que se actualicen las columnas de clave de índice) o , si son tantas que el índice se acerca a una copia de su tabla, use la opción 1 Sugeriría! Además, si agregar ciertas columnas de inclusión no hace ninguna diferencia de rendimiento, es posible que desee omitir la idea de agregarlas :) ¡Verifique que sean útiles!

El número promedio de filas por los mismos valores en las claves (id1, id2 ... idN) también puede ser de cierta importancia.

Tenga en cuenta que si se usa una columna, que se agrega como una columna de índice incluida, en la restricción : siempre que se pueda usar el índice como tal (en función de la restricción contra las columnas de clave de índice), entonces SQL Server coincide la restricción de columna contra el índice (valores de nodo de hoja) en lugar de ir de la manera costosa alrededor de la tabla misma.

Fredrik Solhaug
fuente
18

Las columnas de índice básicas están ordenadas, pero las columnas incluidas no están ordenadas. Esto ahorra recursos para mantener el índice, al tiempo que permite proporcionar los datos en las columnas incluidas para cubrir una consulta. Por lo tanto, si desea cubrir consultas, puede colocar los criterios de búsqueda para ubicar filas en las columnas ordenadas del índice, pero luego "incluir" columnas adicionales sin clasificar con datos que no sean de búsqueda. Definitivamente ayuda a reducir la cantidad de clasificación y fragmentación en el mantenimiento del índice.

onupdatecascade
fuente
7

Las razones por las cuales (incluyendo los datos en el nivel de hoja del índice) se han explicado muy bien. La razón por la que da dos sacudidas al respecto es que, cuando ejecuta su consulta, si no tiene las columnas adicionales incluidas (nueva característica en SQL 2005), el servidor SQL tiene que ir al índice agrupado para obtener las columnas adicionales lo que lleva más tiempo y agrega más carga al servicio de SQL Server, a los discos y a la memoria (la memoria caché del búfer para ser específica) a medida que se cargan nuevas páginas de datos en la memoria, lo que puede eliminar otros datos que se necesitan con mayor frecuencia fuera de la memoria caché del búfer.

mrdenny
fuente
¿Hay alguna manera de demostrar que en realidad está usando menos memoria? es lo que yo esperaría también, pero estoy obteniendo algo de estática sobre esto en el trabajo
Asken
Dado que tiene que cargar la página del montón o el índice agrupado en la memoria, así como la página del índice, lo que significa que está colocando datos duplicados en la memoria, las matemáticas se vuelven bastante simples. En cuanto a una forma de medirlo específicamente, no, no lo hay.
mrdenny
5

Una consideración adicional que no he visto en las respuestas ya dadas, es que las columnas incluidas pueden ser de tipos de datos que no están permitidos como columnas de clave de índice, como varchar (max).

Esto le permite incluir tales columnas en un índice de cobertura. Recientemente tuve que hacer esto para proporcionar una consulta generada por nHibernate, que tenía muchas columnas en SELECT, con un índice útil.

Robin Hames
fuente
3

Una razón para preferir INCLUDEa las columnas clave si no necesita esa columna en la clave es la documentación. Eso hace que la evolución de los índices sea mucho más fácil en el futuro.

Considerando tu ejemplo:

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

Ese índice es mejor si su consulta se ve así:

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

Por supuesto, no debe colocar columnas INCLUDEsi puede obtener un beneficio adicional al tenerlas en la parte clave. Las dos consultas siguientes preferirían la col2columna en la clave del índice.

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

Supongamos que este no es el caso y tenemos col2en la INCLUDEcláusula porque simplemente no hay beneficio de tenerlo en la parte del árbol del índice.

Avance rápido algunos años.

Necesita ajustar esta consulta:

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

Para optimizar esa consulta, el siguiente índice sería excelente:

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

Si verifica qué índices tiene en esa tabla, su índice anterior aún podría estar allí:

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

Ahora lo sabe Col2y Col3no forma parte del árbol de índice y, por lo tanto, no se utiliza para reducir el rango del índice de lectura ni para ordenar las filas. Es bastante seguro agregarlo another_columnal final de la parte clave del índice (después col1). Hay poco riesgo de romper algo:

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

Ese índice se hará más grande, lo que todavía tiene algunos riesgos, pero generalmente es mejor extender los índices existentes en comparación con la introducción de nuevos.

Si tuviera un índice sin INCLUDE, no podría saber qué consultas rompería agregando another_coljusto después Col1.

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

¿Qué sucede si agregas another_colentre Col1y Col2? ¿Otras consultas sufrirán?

Hay otros "beneficios" de INCLUDElas columnas de clave vs. vs. si agrega esas columnas solo para evitar recuperarlas de la tabla . Sin embargo, considero que el aspecto de la documentación es el más importante.

Para responder tu pregunta:

¿Qué pautas sugeriría para determinar si crear un índice de cobertura con o sin la cláusula INCLUDE?

Si agrega una columna al índice con el único propósito de tener esa columna disponible en el índice sin visitar la tabla, póngala en la INCLUDEcláusula.

Si agregar la columna a la clave de índice brinda beneficios adicionales (por ejemplo, para order byo porque puede reducir el rango del índice de lectura), agréguelo a la clave.

Puedes leer una discusión más larga sobre esto aquí:

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

Markus Winand
fuente
2

Existe un límite para el tamaño total de todas las columnas integradas en la definición del índice. Dicho esto, nunca he tenido que crear un índice tan amplio. Para mí, la mayor ventaja es el hecho de que puede cubrir más consultas con un índice que ha incluido columnas, ya que no tienen que definirse en ningún orden en particular. Pensar es como un índice dentro del índice. Un ejemplo sería el StoreID (donde StoreID es de baja selectividad, lo que significa que cada tienda está asociada con muchos clientes) y luego los datos demográficos del cliente (Apellido, Nombre, Fecha de nacimiento): si solo alinea esas columnas en este orden (StoreID, Apellido) , FirstName, DOB), solo puede buscar eficientemente clientes para los que conozca StoreID y LastName.

Por otro lado, definir el índice en StoreID e incluir las columnas LastName, FirstName, DOB le permitiría en esencia hacer dos predicados de índice de búsqueda en StoreID y luego buscar predicado en cualquiera de las columnas incluidas. Esto le permitiría cubrir todas las permutaciones de búsqueda posibles siempre que comience con StoreID.

mEmENT0m0RI
fuente