¿Por qué esta consulta no utiliza un carrete de índice?

23

Estoy haciendo esta pregunta para comprender mejor el comportamiento del optimizador y comprender los límites en torno a los carretes de índice. Supongamos que pongo enteros del 1 al 10000 en un montón:

CREATE TABLE X_10000 (ID INT NOT NULL);
truncate table X_10000;

INSERT INTO X_10000 WITH (TABLOCK)
SELECT TOP 10000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

Y forzar una unión de bucle anidado con MAXDOP 1:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID = b.ID
OPTION (LOOP JOIN, MAXDOP 1);

Esta es una acción poco amigable que se debe tomar hacia SQL Server. Las uniones de bucle anidado a menudo no son una buena opción cuando ambas tablas no tienen índices relevantes. Aquí está el plan:

mala consulta

La consulta tarda 13 segundos en mi máquina con 100000000 filas obtenidas del carrete de la tabla. Sin embargo, no veo por qué la consulta debe ser lenta. El optimizador de consultas tiene la capacidad de crear índices sobre la marcha a través de carretes de índice . Esta consulta parece ser un candidato perfecto para un carrete de índice.

La siguiente consulta devuelve los mismos resultados que la primera, tiene un carrete de índice y finaliza en menos de un segundo:

SELECT *
FROM X_10000 a
CROSS APPLY (SELECT TOP (9223372036854775807) b.ID FROM X_10000 b WHERE a.ID = b.ID) ca
OPTION (LOOP JOIN, MAXDOP 1);

solución 1

Esta consulta también tiene un carrete de índice y termina en menos de un segundo:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (LOOP JOIN, MAXDOP 1);

solución 2

¿Por qué la consulta original no tiene un carrete de índice? ¿Hay algún conjunto de sugerencias documentadas o indocumentadas o marcas de seguimiento que le den un carrete de índice? Encontré esta pregunta relacionada , pero no responde completamente a mi pregunta y no puedo hacer que la misteriosa marca de seguimiento funcione para esta consulta.

Joe Obbish
fuente

Respuestas:

20

Como saben, la búsqueda del optimizador no es exhaustiva. Intenta cosas que tienen sentido en contexto y que con frecuencia pagan dividendos en consultas reales. Forzar una unión de bucle entre dos tablas de montón no indexadas de una sola columna no es tal escenario. Dicho esto, aquí hay algunos detalles:

A SQL Server le gusta que la transformación se aplique a las uniones temprano, porque conoce más trucos con las uniones. Más adelante, puede explorar la conversión de la unión nuevamente a una aplicación. La diferencia entre los dos parámetros correlacionados (referencias externas). Las aplicaciones tienen sentido cuando hay un índice adecuado en el lado interno. Su ejemplo no tiene índices, por lo que no se convence al optimizador para que explore la traducción a una solicitud.

Una combinación simple (sin aplicación) tiene el predicado de combinación en el operador de combinación en lugar de referencias externas. La optimización de spool para una no aplicación suele ser un spool de tabla diferido, ya que no hay un predicado en el lado interno, solo en la unión.

El optimizador no considera construir un índice sobre la marcha para permitir una aplicación; más bien, la secuencia de eventos suele ser la inversa: transformar para aplicar porque existe un buen índice.

A veces, puede alentar una aplicación en lugar de una combinación utilizando la APPLYsintaxis en su consulta. El indicador de traza no documentado 9114 puede ayudar en esto al disuadir al optimizador de traducir una aplicación lógica a una unión por adelantado. Por ejemplo:

SELECT * 
FROM dbo.X_1000 AS a
CROSS APPLY (SELECT * FROM dbo.X_1000 AS b WHERE b.ID = a.ID) AS b
OPTION (QUERYTRACEON 9114);

Plan de carrete

Se prefiere un carrete de índice para aplicar porque la referencia externa significa que la selección se aplica en el lado interno de la unión. A menudo verá esto a través, SelToIndexOnTheFlypero existen otras rutas. Vea mi artículo The Eager Index Spool y The Optimizer .

Paul White dice GoFundMonica
fuente