Esto es un poco amplio, pero creo que entiendo la verdadera pregunta y responderé en consecuencia. Sin embargo, solo voy a hablar sobre el carrete de tabla vs índice. No creo que sea correcto verlo allí como una elección entre carretes de tabla e índice. Como sabe, en un solo subárbol es posible obtener un carrete de índice, un carrete de tabla o tanto un carrete de índice como un carrete de tabla. Creo que generalmente es correcto decir que obtienes un carrete de índice en las siguientes condiciones:
- El optimizador de consultas tiene una razón para transformar una unión en una aplicación
- El optimizador de consultas realmente realiza la transformación a la aplicación
- El optimizador de consultas usa la regla para agregar una cola de índice (como mínimo, la cola de índice debe ser segura de usar)
- Se selecciona el plan con el carrete de índice.
Puede ver la mayoría de estos con demostraciones simples. Comience creando un par de montones:
DROP TABLE IF EXISTS dbo.X_10000_VARCHAR_901;
CREATE TABLE dbo.X_10000_VARCHAR_901 (ID VARCHAR(901) NOT NULL);
INSERT INTO dbo.X_10000_VARCHAR_901 WITH (TABLOCK)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
DROP TABLE IF EXISTS dbo.X_10000_VARCHAR_800;
CREATE TABLE dbo.X_10000_VARCHAR_800 (ID VARCHAR(800) NOT NULL);
INSERT INTO dbo.X_10000_VARCHAR_800 WITH (TABLOCK)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
Para la primera consulta, no hay nada que buscar:
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
CROSS JOIN dbo.X_10000_VARCHAR_901 b
OPTION (MAXDOP 1);
Por lo tanto, no hay ninguna razón para que el optimizador transforme la unión en una aplicación. Terminas con un carrete de mesa debido a razones de costos. Entonces esta consulta falla la primera prueba.
Para la siguiente consulta, es justo esperar que el optimizador tenga una razón para considerar una solicitud:
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
INNER JOIN dbo.X_10000_VARCHAR_901 b ON a.ID = b.ID
OPTION (LOOP JOIN, MAXDOP 1);
Pero no está destinado a ser:
Esta consulta falla la segunda prueba. Una explicación completa está aquí . Citando la parte más relevante:
El optimizador no considera construir un índice sobre la marcha para permitir una aplicación; más bien la secuencia de eventos suele ser al revés: transformar para aplicar porque existe un buen índice.
Puedo reescribir la consulta para alentar al optimizador a considerar una solicitud:
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
INNER JOIN dbo.X_10000_VARCHAR_901 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (MAXDOP 1);
Pero todavía no hay una cola de índice:
Esta consulta falla la tercera prueba. En SQL Server 2014 había un límite de longitud de clave de índice de 900 bytes. Esto se extendió en SQL Server 2016 pero solo para índices no agrupados. El índice de una cola es un índice agrupado, por lo que el límite permanece en 900 bytes . En cualquier caso, la regla de spool de índice no se puede aplicar porque podría provocar un error durante la ejecución de la consulta.
Reducir la longitud del tipo de datos a 800 finalmente proporciona un plan con un carrete de índice:
El plan de spool de índice, no es sorprendente, tiene un costo significativamente más barato que un plan sin spool: 89.7603 unidades frente a 598.832 unidades. Puede ver la diferencia con la QUERYRULEOFF BuildSpool
sugerencia de consulta no documentada :
Esta no es una respuesta completa, pero espero que sea algo de lo que estaba buscando.