Muy buena pregunta ya que es un concepto tan importante. Sin embargo, este es un gran tema y lo que voy a mostrar es una simplificación para que pueda comprender los conceptos básicos.
En primer lugar, cuando ve una tabla de pensamiento de índice agrupado . En el servidor SQL, si una tabla no contiene un índice agrupado, es un montón. Crear un índice agrupado en la tabla en realidad transforma la tabla en una estructura tipo b-tree. Su índice agrupado ES su tabla, no está separado de la tabla
¿Alguna vez se preguntó por qué solo puede tener un índice agrupado? Bueno, si tuviéramos dos índices agrupados necesitaríamos dos copias de la tabla. Contiene los datos después de todo.
Voy a tratar de explicar esto usando un ejemplo simple.
NOTA: Creé la tabla en este ejemplo y la llené con más de 3 millones de entradas aleatorias. Luego ejecutó las consultas reales y pegó los planes de ejecución aquí.
Lo que realmente necesita comprender es la notación O o la eficiencia operativa . Supongamos que tiene la siguiente tabla.
CREATE TABLE [dbo].[Customer](
[CustomerID] [int] IDENTITY(1,1) NOT NULL,
[CustomerName] [varchar](100) NOT NULL,
[CustomerSurname] [varchar](100) NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED
(
[CustomerID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF
, IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON
, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Entonces, aquí tenemos una tabla básica con una clave agrupada en CustomerID (la clave principal está agrupada de forma predeterminada). Por lo tanto, la tabla se organiza / ordena en función de la clave principal CustomerID. Los niveles intermedios contendrán los valores de CustomerID. Las páginas de datos contendrán la fila completa, por lo tanto, es la fila de la tabla.
También crearemos un índice no agrupado en el campo CustomerName. El siguiente código lo hará.
CREATE NONCLUSTERED INDEX [ix_Customer_CustomerName] ON [dbo].[Customer]
(
[CustomerName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF
, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF
, DROP_EXISTING = OFF, ONLINE = OFF
, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
Entonces, en este índice, encontrará en las páginas de datos / nodos de nivel de hoja un puntero a los niveles intermedios en el índice agrupado. El índice se organiza / ordena alrededor del campo CustomerName. Por lo tanto, el nivel intermedio contiene los valores de CustomerName y el nivel de hoja contendrá el puntero (estos valores de puntero son en realidad los valores de clave principal o la columna CustomerID).
Correcto, si ejecutamos la siguiente consulta:
SELECT * FROM Customer WHERE CustomerID = 1
SQL probablemente leerá el índice agrupado a través de una operación de búsqueda. Una operación de búsqueda es una búsqueda binaria que es mucho más eficiente que una exploración que es una búsqueda secuencial. Entonces, en nuestro ejemplo anterior, se lee el índice y, mediante una búsqueda binaria, SQL puede eliminar los datos que no coinciden con los criterios que estamos buscando. Vea la captura de pantalla adjunta para el plan de consulta.
Entonces, el número de operaciones o la notación O para la operación de búsqueda es la siguiente:
- Realice una búsqueda binaria en el índice agrupado comparando el valor buscado con los valores en el nivel intermedio.
- Devuelva los valores que coinciden (recuerde, ya que el índice agrupado contiene todos los datos, puede devolver todas las columnas del índice, ya que son los datos de la fila)
Entonces son dos operaciones. Sin embargo, si ejecutamos la siguiente consulta:
SELECT * FROM Customer WHERE CustomerName ='John'
SQL ahora usará el índice no agrupado en CustomerName para realizar la búsqueda. Sin embargo, dado que este es un índice no agrupado, no contiene todos los datos de la fila.
Por lo tanto, SQL realizará la búsqueda en los niveles intermedios para encontrar los registros que coinciden y luego realizará una búsqueda utilizando los valores devueltos para realizar otra búsqueda en el índice agrupado (también conocido como la tabla) para recuperar los datos reales. Esto suena confuso, lo sé, pero sigue leyendo y todo quedará claro.
Dado que nuestro índice no agrupado solo contiene el campo CustomerName (los valores de campo indexados almacenados en los nodos intermedios) y el puntero a los datos que es CustomerID, el índice no tiene registro del CustomerSurname. El CustomerSurname se debe obtener del índice o la tabla agrupados.
Cuando ejecuto esta consulta obtengo el siguiente plan de ejecución:
Hay dos cosas importantes que debes notar en la captura de pantalla anterior
- SQL dice que me falta un índice (el texto en verde). SQL sugiere que cree un índice en CustomerName que incluya CustomerID y CustomerSurname.
- También verá que el 99% del tiempo de la consulta se dedica a realizar una búsqueda de clave en el índice de clave principal / índice agrupado.
¿Por qué SQL vuelve a sugerir el índice en CustomerName? Bueno, dado que el índice contiene solo el CustomerID y el CustomerName SQL todavía tiene que encontrar el CustomerSurname de la tabla / índices agrupados.
Si creáramos el índice e incluyéramos la columna CustomerSurname en el índice, SQL podría satisfacer la consulta completa simplemente leyendo el índice no agrupado. Es por eso que SQL sugiere que cambie mi índice no agrupado.
Aquí puede ver la operación adicional que SQL debe hacer para obtener la columna CustomerSurname de la clave agrupada
Por lo tanto, el número de operaciones es el siguiente:
- Realice una búsqueda binaria en un índice no agrupado comparando el valor buscado con los valores en el nivel intermedio
- Para los nodos que coinciden, lea el nodo de nivel de hoja que contendrá el puntero para los datos en el índice agrupado (los nodos de nivel de hoja contendrán los valores de clave principal por cierto).
- Para cada valor devuelto, lea el índice agrupado (la tabla) para obtener los valores de la fila aquí, leeríamos el CustomerSurname.
- Devolver filas coincidentes
Eso son 4 operaciones para obtener los valores. El doble de la cantidad de operaciones necesarias en comparación con la lectura del índice agrupado. Le muestra que su índice agrupado es su índice más poderoso, ya que contiene todos los datos.
Tan solo para aclarar un último punto. ¿Por qué digo que el puntero en el índice no agrupado es el valor de la clave primaria? Bueno, para demostrar que los nodos de nivel de hoja del índice no agrupado contienen el valor de la clave primaria, cambio mi consulta a:
SELECT CustomerID
FROM Customer
WHERE CustomerName='Jane'
En esta consulta, SQL puede leer el CustomerID del índice no agrupado. No necesita hacer una búsqueda en el índice agrupado. Esto se puede ver en el plan de ejecución que se ve así.
Observe la diferencia entre esta consulta y la consulta anterior. No hay búsqueda. SQL puede encontrar todos los datos en el índice no agrupado
Esperemos que pueda comenzar a comprender que el índice agrupado es la tabla y los índices no agrupados NO contienen todos los datos. La indexación acelerará las selecciones debido al hecho de que se pueden realizar búsquedas binarias, pero solo los índices agrupados contienen todos los datos. Por lo tanto, una búsqueda en un índice no agrupado casi siempre dará como resultado que los valores se carguen desde el índice agrupado. Estas operaciones adicionales hacen que los índices no agrupados sean menos eficientes que un índice agrupado.
Espero que esto aclare las cosas. Si algo no tiene sentido, publique un comentario e intentaré aclararlo. Es bastante tarde aquí y mi cerebro se siente un poco plano. Tiempo para un toro rojo.