Los planes de consulta con filtros de mapa de bits a veces pueden ser difíciles de leer. Del artículo de BOL para las secuencias de reparto (énfasis mío):
El operador Repartition Streams consume múltiples flujos y produce múltiples flujos de registros. El contenido y el formato del registro no se modifican. Si el optimizador de consultas utiliza un filtro de mapa de bits, se reduce el número de filas en la secuencia de salida.
Además, un artículo sobre filtros de mapa de bits también es útil:
Al analizar un plan de ejecución que contiene un filtro de mapa de bits, es importante comprender cómo fluyen los datos a través del plan y dónde se aplica el filtrado. El filtro de mapa de bits y el mapa de bits optimizado se crean en el lado de entrada de compilación (la tabla de dimensiones) de una unión hash; sin embargo, el filtrado real generalmente se realiza dentro del operador de paralelismo, que está en el lado de entrada de la sonda (la tabla de hechos) de la unión hash. Sin embargo, cuando el filtro de mapa de bits se basa en una columna de enteros, el filtro se puede aplicar directamente a la operación de exploración de índice o tabla inicial en lugar del operador de paralelismo. Esta técnica se llama optimización en fila.
Creo que eso es lo que estás observando con tu consulta. Es posible crear una demostración relativamente simple para mostrar un operador de flujos de reparto que reduce una estimación de cardinalidad, incluso cuando el operador de mapa de bits está en IN_ROW
contra de la tabla de hechos. Preparación de datos:
create table outer_tbl (ID BIGINT NOT NULL);
INSERT INTO outer_tbl WITH (TABLOCK)
SELECT TOP (1000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values;
create table inner_tbl_1 (ID BIGINT NULL);
create table inner_tbl_2 (ID BIGINT NULL);
INSERT INTO inner_tbl_1 WITH (TABLOCK)
SELECT (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) / 2000000 - 2) NUM
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
INSERT INTO inner_tbl_2 WITH (TABLOCK)
SELECT (ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) / 2000000 - 2) NUM
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
Aquí hay una consulta que no debe ejecutar:
SELECT *
FROM outer_tbl o
INNER JOIN inner_tbl_1 i ON o.ID = i.ID
INNER JOIN inner_tbl_2 i2 ON o.ID = i2.ID
OPTION (HASH JOIN, QUERYTRACEON 9481, QUERYTRACEON 8649);
Subí el plan . Eche un vistazo al operador cerca de inner_tbl_2
:
También puede encontrar útil la segunda prueba en Hash Joins on Nullable Columns por Paul White.
Hay algunas inconsistencias en cómo se aplica la reducción de filas. Solo pude verlo en un plan con al menos tres tablas. Sin embargo, la reducción en las filas esperadas parece razonable con la distribución de datos correcta. Suponga que la columna unida en la tabla de hechos tiene muchos valores repetidos que no están presentes en la tabla de dimensiones. Un filtro de mapa de bits podría eliminar esas filas antes de que lleguen a la unión. Para su consulta, la estimación se reduce a 1. La forma en que las filas se distribuyen entre la función hash proporciona una buena pista:
En base a eso, sospecho que tiene muchos valores repetidos para la Object1.Column21
columna. Si las columnas repetidas no están en el histograma de estadísticas, Object4.Column19
entonces SQL Server podría obtener la estimación de cardinalidad muy incorrecta.
Creo que debería preocuparse porque podría ser posible mejorar el rendimiento de la consulta. Por supuesto, si la consulta cumple con el tiempo de respuesta o los requisitos de SLA, entonces puede que no valga la pena investigar más. Sin embargo, si desea investigar más a fondo, hay algunas cosas que puede hacer (además de actualizar las estadísticas) para tener una idea de si el optimizador de consultas elegiría un mejor plan si tuviera mejor información. Puede colocar los resultados de la unión entre Database1.Schema1.Object10
y Database1.Schema1.Object11
en una tabla temporal y ver si continúa obteniendo uniones de bucle anidadas. Puede cambiar esa unión a una LEFT OUTER JOIN
para que el optimizador de consultas no reduzca el número de filas en ese paso. Puede agregar una MAXDOP 1
pista a su consulta para ver qué sucede. Podrías usarTOP
junto con una tabla derivada para forzar que la unión vaya al último, o incluso podría comentar la unión desde la consulta. Esperemos que estas sugerencias sean suficientes para comenzar.
Con respecto al elemento de conexión en la pregunta, es extremadamente improbable que esté relacionado con su pregunta. Ese problema no tiene que ver con estimaciones de filas pobres. Tiene que ver con una condición de carrera en paralelo que hace que se procesen demasiadas filas en el plan de consulta detrás de escena. Aquí parece que su consulta no está haciendo ningún trabajo adicional.