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;
El predicado SARGable en Score no se inserta dentro del CTE. Está en un operador de filtro mucho más tarde en el plan.
Lo cual me parece extraño, ya que ORDER BY
está 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.
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
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
: