¿Cuándo se pueden insertar predicados SARGable en una tabla CTE o derivada?


15

Saco terrero

Mientras trabajaba en las publicaciones de blog de calidad superior, me encontré con un comportamiento optimizador que encontré realmente irritante . No tengo una explicación inmediata, al menos no estoy contento, así que la pongo aquí en caso de que aparezca alguien inteligente.

Si desea seguir, puede obtener la versión 2013 del volcado de datos de desbordamiento de pila aquí . Estoy usando la tabla Comentarios, con un índice adicional.

CREATE INDEX [ix_ennui] ON [dbo].[Comments] ( [UserId], [Score] DESC );

Consulta uno

Cuando consulto la tabla así, obtengo un plan de consulta extraño .

WITH x
    AS
     (
         SELECT   TOP 101
                  c.UserId, c.Text, c.Score
         FROM     dbo.Comments AS c
         ORDER BY c.Score DESC
     )
SELECT *
FROM   x
WHERE  x.Score >= 500;

NUECES

El predicado SARGable en Score no se inserta dentro del CTE. Está en un operador de filtro mucho más tarde en el plan.

NUECES

Lo cual me parece extraño, ya que ORDER BYestá en la misma columna que el filtro.

Consulta dos

Si cambio la consulta, se empuja.

WITH x
    AS
     (
         SELECT   c.UserId, c.Text, c.Score
         FROM     dbo.Comments AS c
     )
SELECT TOP 101 *
FROM   x
WHERE  x.Score >= 500
ORDER BY x.Score DESC;

El plan de consulta también cambia y se ejecuta mucho más rápido, sin derrames en el disco. Ambos producen los mismos resultados, con el predicado en la exploración de índice no agrupado.

NUECES

NUECES

Consulta tres

Esto es equivalente a escribir la consulta de esta manera:

SELECT   TOP 101
         c.UserId, c.Text, c.Score
FROM     dbo.Comments AS c
WHERE c.Score >= 500
ORDER BY c.Score DESC;

Consulta cuatro

El uso de una tabla derivada obtiene el mismo plan de consulta "incorrecto" que la consulta CTE inicial

SELECT *
FROM   (   SELECT   TOP 101
                    c.UserId, c.Text, c.Score
           FROM     dbo.Comments AS c
           ORDER BY c.Score DESC ) AS x
WHERE x.Score >= 500;

Las cosas se ponen aún más raras cuando ...

Cambio la consulta para ordenar los datos ascendentes y el filtro a <=.

Para evitar hacer esta pregunta demasiado larga, voy a armar todo.

Consultas

--Derived table
SELECT *
FROM   (   SELECT   TOP 101
                    c.UserId, c.Text, c.Score
           FROM     dbo.Comments AS c
           ORDER BY c.Score ASC ) AS x
WHERE x.Score <= 500;


--TOP inside CTE
WITH x
    AS
     (
         SELECT   TOP 101
                  c.UserId, c.Text, c.Score
         FROM     dbo.Comments AS c
         ORDER BY c.Score ASC
     )
SELECT *
FROM   x
WHERE  x.Score <= 500;


--Written normally
SELECT   TOP 101
         c.UserId, c.Text, c.Score
FROM     dbo.Comments AS c
WHERE c.Score <= 500
ORDER BY c.Score ASC;

--TOP outside CTE
WITH x
    AS
     (
         SELECT   c.UserId, c.Text, c.Score
         FROM     dbo.Comments AS c
     )
SELECT TOP 101 *
FROM   x
WHERE  x.Score <= 500
ORDER BY x.Score ASC;

Planes

Plan de enlace .

NUECES

Tenga en cuenta que ninguna de estas consultas aprovecha el índice no agrupado; lo único que cambia aquí es la posición del operador del filtro. En ningún caso el predicado es empujado al acceso al índice.

¡Una pregunta aparece!

¿Hay alguna razón por la que un predicado SARGable puede ser empujado en algunos escenarios y no en otros? Las diferencias dentro de las consultas ordenadas en orden descendente son interesantes, pero las diferencias entre ellas y las ascendentes son extrañas.

Para cualquier persona interesada, estos son los planes con solo un índice sobre Score:

Respuestas:


11

Hay algunos problemas en juego aquí.

Empujando predicados pasado TOP

Actualmente, el optimizador no puede empujar un predicado más allá de a TOP, incluso en los casos limitados en los que sería seguro hacerlo *. Esta limitación explica el comportamiento de todas las consultas en la pregunta donde el predicado está en un alcance mayor que el TOP.

La solución es realizar la reescritura manualmente. El problema fundamental es similar al caso de empujar predicados más allá de una función de ventana , excepto que no existe una regla especializada correspondiente SelOnSeqPrj.

Mi opinión personal es que una regla de exploración como SelOnTopsigue sin implementarse porque las personas han escrito deliberadamente consultas TOPen un esfuerzo por proporcionar una especie de 'valla de optimización'.

* En general, esto significa que el predicado debe aparecer en la ORDER BYcláusula asociada con el TOP, y la dirección de cualquier desigualdad debe coincidir con la dirección de la clasificación. La transformación también debería tener en cuenta el comportamiento de clasificación de NULL en SQL Server. En general, las limitaciones probablemente significan que esta transformación generalmente no sería lo suficientemente útil en la práctica como para justificar los esfuerzos de exploración adicionales.

Problemas de costos

Los planes de ejecución restantes en la pregunta pueden explicarse como opciones basadas en el costo debido a la distribución de valores en la Scorecolumna (muchas más filas <= 500 que> = 500) y el efecto del objetivo de fila introducido por elTOP .

Por ejemplo, la consulta:

--Written normally
SELECT TOP (101)
    c.UserId, 
    c.[Text],
    c.Score
FROM dbo.Comments AS c
WHERE
    c.Score <= 500
ORDER BY
    c.Score ASC;

... produce un plan con un predicado aparentemente sin empujar en un filtro:

filtro tardío debido al gol de la fila

Tenga en cuenta que se estima que la Clasificación produce 101 filas. Este es el efecto del objetivo de fila agregado por la parte superior. Esto afecta el costo estimado de la clasificación y el filtro lo suficiente como para que parezca que esta es la opción más barata. El costo estimado de este plan es de 2401.39 unidades.

Si deshabilitamos los objetivos de fila con una sugerencia de consulta:

--Written normally
SELECT TOP (101)
    c.UserId, 
    c.[Text],
    c.Score
FROM dbo.Comments AS c
WHERE
    c.Score <= 500
ORDER BY
    c.Score ASC
OPTION (USE HINT ('DISABLE_OPTIMIZER_ROWGOAL'));

... el plan de ejecución producido es:

plan sin gol de fila

El predicado ha sido introducido en el escaneo como un predicado residual no sargable, y el costo de todo el plan es de 2402.32 unidades.

Tenga en cuenta que <= 500no se espera que el predicado filtre ninguna fila. Si hubiera elegido un número menor, por ejemplo <= 50, el optimizador hubiera preferido el plan de predicado impulsado independientemente del efecto de objetivo de fila.

Para la consulta con Score DESCy un Score >= 500predicado:

--Written normally
SELECT TOP (101)
    c.UserId, 
    c.[Text],
    c.Score
FROM dbo.Comments AS c
WHERE
    c.Score >= 500
ORDER BY
    c.Score DESC;

Ahora se espera que el predicado sea muy selectivo, por lo que el optimizador elige empujar el predicado y usar el índice no agrupado con búsquedas:

predicado selectivo

Nuevamente, el optimizador consideró múltiples alternativas y eligió esta como la opción aparentemente más barata, como de costumbre.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.