¿Por qué mi ORDER BY ordena dos tablas antes del EXCEPTO (lento) y no después (rápido)?

12

Rompecabezas optimizador de consultas de SQL Server 2008 R2

Tenemos dos tablas, ambas con 9 millones de filas. 70,000 filas son diferentes, las otras son iguales.

Esto es rápido, 13 segundos,

select * from bigtable1
except select * from similar_bigtable2

Esto ordena la salida y también es rápido, 13 segundos también,

select * into #q from bigtable1
except select * from similar_bigtable2
select * from #q order by sort_column

Si bien esto es enormemente lento:

;with q as (
    select * from bigtable1
    except select * from similar_bigtable2
)
select * from q order by sort_column

E incluso un "truco" que a veces utilizo para insinuar que SQL Server necesita calcular previamente una cierta parte de la consulta antes de continuar, no funciona y resulta en una consulta lenta también:

;with q as (
    select top 100 percent * from bigtable1
    except select * from similar_bigtable2
)
select * from q order by sort_column

Mirando los planes de consulta, no es difícil encontrar el motivo:

Plan de consulta Consultar plan con ORDER BY

SQL Server coloca dos tipos de 9 millones de filas antes del hashmatch, mientras que preferiría que hubiera agregado solo un tipo de 70,000 filas después del hashmatch.

Entonces, la pregunta: ¿cómo puedo indicarle al optimizador de consultas que haga eso?

thomaspaulb
fuente
3
No se ordena antes del hashmatch, se ordena y luego se combina (no es un hash-join). ¿Tal vez hay una pista para forzar un hash-join (o evitar una combinación de unión)?
Thilo
3
Parece que el optimizador de consultas de SQL Server determinó que ordenar los datos era beneficioso, por lo que podría usar la combinación de combinación mucho más rápida (que solo funciona para datos ordenados) en lugar de la
combinación de unión de
99
¿Has probado alternativas a EXCEPT(por ejemplo OUTER JOIN)? Me doy cuenta de que la sintaxis es menos conveniente, pero es posible que pueda jugar con pistas de índice / unión mejor allí (o puede que no sea necesario). La alternativa que está usando ahora (primero en una tabla #temp) es una solución alternativa de último recurso, pero en algunos casos es la única forma de forzar al optimizador a separar por completo dos partes de una consulta de la manera que desee.
Aaron Bertrand

Respuestas:

1

La principal diferencia entre estos dos planes de consulta es, de hecho, la diferencia de Hash Match y Merge Join. Hash Match es más eficiente y, como puede ver, la consulta se ejecuta más rápido en la opción 1 (sin usar CTE).

CTE es una gran herramienta, pero parece no ser eficiente en dos casos, Predicados complejos o Clave principal / secundaria no única. En su caso, no hay una clave única y el servidor SQL debe ordenar primero los conjuntos de datos para poder cumplir con sus requisitos. Eche un vistazo al siguiente enlace que le brinda más información sobre este tema: http://blogs.msdn.com/b/sqlcat/archive/2011/04/28/optimize-recursive-cte-query.aspx

Por lo tanto, parece que debe aceptar su lentitud o reescribir la lógica con el ciclo WHILE, que puede ser más eficiente.

Cielo
fuente
0

Intenta esto, ¿algo mejor?

select * from
(
    select * from bigtable1
    except 
    select * from similar_bigtable2
) t
order by sort_column
Gordon Bell
fuente
0

Esta no es una solución ideal, pero si no puede estructurar el tsql para generar un plan eficiente, puede establecer una guía de plan para forzar el plan que desea. Hacer esto significaría que si un plan más eficiente está disponible, SQL no lo considerará, pero es una opción.

Cfradenburg
fuente