Tengo una mesa con unas pocas docenas de filas. La configuración simplificada está siguiendo
CREATE TABLE #data ([Id] int, [Status] int);
INSERT INTO #data
VALUES (100, 1), (101, 2), (102, 3), (103, 2);
Y tengo una consulta que une esta tabla a un conjunto de filas construidas de valores de tabla (hechas de variables y constantes), como
DECLARE @id1 int = 101, @id2 int = 105;
SELECT
COALESCE(p.[Code], 'X') AS [Code],
COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
(@id1, 'A'),
(@id2, 'B')
) p([Id], [Code])
FULL JOIN #data d ON d.[Id] = p.[Id];
El plan de ejecución de consultas muestra que la decisión del optimizador es usar la FULL LOOP JOIN
estrategia, lo que parece apropiado, ya que ambas entradas tienen muy pocas filas. Sin embargo, una cosa que noté (y no puedo estar de acuerdo) es que las filas de TVC se están poniendo en cola (vea el área del plan de ejecución en el cuadro rojo).
¿Por qué el optimizador introduce el carrete aquí, cuál es la razón para hacerlo? No hay nada complejo más allá del carrete. Parece que no es necesario. ¿Cómo deshacerse de él en este caso, cuáles son las formas posibles?
El plan anterior se obtuvo el
Microsoft SQL Server 2014 (SP2-CU11) (KB4077063) - 12.0.5579.0 (X64)
fuente
Respuestas:
Lo que está más allá del spool no es una simple referencia de tabla, que simplemente podría duplicarse cuando se genera la alternativa de unión izquierda / anti semi unión .
Puede parecerse un poco a una tabla (Constant Scan) pero para el optimizador * es una
UNION ALL
de las filas separadas en laVALUES
cláusula.La complejidad adicional es suficiente para que el optimizador elija poner en cola y reproducir las filas de origen, y no reemplazar la cola con un simple "table get" más adelante. Por ejemplo, la transformación inicial de la unión completa se ve así:
Observe los carretes adicionales introducidos por la transformación general. Los carretes encima de una tabla simple se limpian más tarde por la regla
SpoolGetToGet
.Si el optimizador tuviera una
SpoolConstGetToConstGet
regla correspondiente , en principio podría funcionar como lo desee.Use una tabla real (temporal o variable), o escriba la transformación desde la unión completa manualmente, por ejemplo:
Plan para la reescritura manual:
Esto tiene un costo estimado de 0.0067201 unidades, en comparación con 0.0203412 unidades para el original.
* Se puede observar como a
LogOp_UnionAll
en el Árbol convertido (TF 8605). En el árbol de entrada (TF 8606) es aLogOp_ConstTableGet
. El árbol convertido muestra el árbol de elementos de expresión del optimizador después del análisis, la normalización, la algebrización, el enlace y algunos otros trabajos preparatorios. El árbol de entrada muestra los elementos después de la conversión a la forma normal de negación (conversión NNF), el colapso constante del tiempo de ejecución y algunos otros bits y oscilaciones. La conversión NNF incluye lógica para colapsar uniones lógicas y obtener tablas comunes, entre otras cosas.fuente
El carrete de tabla simplemente está creando una tabla a partir de los dos conjuntos de tuplas presentes en la
VALUES
cláusula.Puede eliminar el carrete insertando esos valores en una tabla temporal primero, de esta manera:
Al observar el plan de ejecución de su consulta, vemos que la lista de salida contiene dos columnas que usan el
Union
prefijo; Esta es una pista de que el spool está creando una tabla desde una fuente sindicalizada:los
FULL OUTER JOIN
requiere SQL Server para acceder a los valores enp
dos veces, una para cada "lado" de la combinación. La creación de un spool permite que los bucles internos resultantes se unan para acceder a los datos en spool.Curiosamente, si reemplaza el
FULL OUTER JOIN
con ayLEFT JOIN
aRIGHT JOIN
, yUNION
los resultados juntos, SQL Server no usa un spool.Tenga en cuenta que no sugiero usar la
UNION
consulta anterior; para conjuntos de entrada más grandes, puede que no sea más eficiente que el simpleFULL OUTER JOIN
que ya tiene.fuente