¿Por qué estas consultas similares utilizan diferentes fases de optimización (procesamiento de transacciones versus plan rápido)?

12

El código de ejemplo en este elemento de conexión

Muestra un error donde

SELECT COUNT(*)
FROM   dbo.my_splitter_1('2') L1
       INNER JOIN dbo.my_splitter_1('') L2
         ON L1.csv_item = L2.csv_item

Devuelve los resultados correctos. Pero lo siguiente devuelve resultados incorrectos (en 2014 usando el nuevo Estimador de cardinalidad)

SELECT
    (SELECT COUNT(*)
    FROM dbo.my_splitter_1('2') L1
     INNER JOIN dbo.my_splitter_1('') L2
        ON L1.csv_item = L2.csv_item)

Como carga incorrectamente los resultados para L2 en un carrete de subexpresión común, luego reproduce el resultado de eso para el resultado L1.

Tenía curiosidad sobre por qué la diferencia de comportamiento entre las dos consultas. La marca de seguimiento 8675 muestra que entra el que funciona search(0) - transaction processingy el que falla search(1) - quick plan.

Por lo tanto, supongo que la disponibilidad de reglas de transformación adicionales está detrás de la diferencia de comportamiento (deshabilitar BuildGbApply o GenGbApplySimple parece solucionarlo, por ejemplo).

Pero, ¿por qué los dos planes para estas consultas muy similares encuentran diferentes fases de optimización? Por lo que he leído, se search (0)requieren al menos tres tablas y esa condición ciertamente no se cumple en el primer ejemplo.

Martin Smith
fuente

Respuestas:

7

Cada etapa tiene condiciones de entrada. "Tener al menos tres referencias de tabla" es una de las condiciones de entrada de las que hablamos cuando damos ejemplos simples, pero no es la única.

En general, solo se permiten uniones y uniones básicas para la entrada a la búsqueda 0; las subconsultas escalares, las semiuniones, etc. impiden la entrada a la búsqueda 0. Esta etapa es realmente para las formas de consulta de tipo OLTP muy comunes. Las reglas necesarias para explorar las cosas menos comunes simplemente no están habilitadas. Su consulta de ejemplo tiene una subconsulta escalar, por lo que falla la entrada.

También depende de cómo cuente las referencias de tabla. Nunca he analizado esto en profundidad con las funciones, pero es posible que la lógica cuente las funciones de valor de tabla, así como las variables de tabla que producen. Incluso podría estar contando la referencia de la tabla dentro de la función en sí; no estoy seguro; aunque sé que las funciones son un trabajo duro en general.

El error con GenGbApplySimplees feo. Esta forma de plan siempre fue una posibilidad, pero se rechazó por razones de costo hasta que el cambio a 100 filas asumió la cardinalidad de la variable de tabla. Es posible forzar la forma de plan problemática en el CE anterior a 2014 con una USE PLANpista, por ejemplo.

Tienes razón acerca de que el nuevo elemento Connect es el mismo problema informado anteriormente .

Para proporcionar un ejemplo, la siguiente consulta califica para la búsqueda 0:

DECLARE @T AS table (c1 integer NULL);

SELECT U.c1, rn = ROW_NUMBER() OVER (ORDER BY U.c1) 
FROM 
(
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
) AS U;

Hacer un pequeño cambio para incluir una subconsulta escalar significa que va directamente a la búsqueda 1:

DECLARE @T AS table (c1 integer NULL);

SELECT U.c1, rn = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -- Changed!
FROM 
(
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
    UNION
    SELECT c1 FROM @T AS T
) AS U;
Paul White 9
fuente