Tengo la siguiente consulta:
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
La consulta anterior se completa en tres segundos.
Si la consulta anterior devuelve algún valor, queremos que el procedimiento almacenado salga, así que lo reescribí como a continuación:
If Exists(
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
)
Begin
Raiserror('Source missing',16,1)
Return
End
Sin embargo, esto está tomando 10 minutos.
Puedo reescribir la consulta anterior como a continuación, que también se completa en menos de 3 segundos:
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source
if @@rowcount >0
Begin
Raiserror('Source missing',16,1)
Return
End
El problema con la reescritura anterior es que la consulta anterior es parte de un procedimiento almacenado más grande y devuelve múltiples conjuntos de resultados. En C #, iteramos a través de cada conjunto de resultados y realizamos algunos procesamientos.
Lo anterior devuelve un conjunto de resultados vacío, por lo que si sigo este enfoque, tengo que cambiar mi C # y volver a implementar.
Entonces mi pregunta es:
¿Por qué usar solo
IF EXISTS
cambia el plan para tomar tanto tiempo?
A continuación se encuentran los detalles que pueden ayudarlo y avíseme si necesita algún detalle:
- Crear tabla y script de estadísticas para obtener el mismo plan que el mío
- Plan de ejecución lenta
Plan de ejecución rápida
Plan lento con Brentozar Pegar el plan
Plan rápido con Brentozar Pegar el plan
Nota: Ambas consultas son iguales (usando parámetros), la única diferencia es EXISTS
(aunque podría haber cometido algunos errores al anonimizar).
Los scripts de creación de tablas están a continuación:
http://pastebin.com/CgSHeqXc - estadísticas de mesa pequeña
http://pastebin.com/GUu9KfpS - estadísticas de mesa grande
fuente
Respuestas:
Como ha sido explicado por Paul White en su blog: En el interior del optimizador: Objetivos de fila en profundidad las
EXISTS
introduce un gol fila, que prefiereNESTED LOOPS
oMERGE JOIN
más deHASH MATCH
En su consulta, esto aparentemente introduce bucles anidados y elimina el paralelismo, lo que resulta en un plan más lento.
Por lo tanto, es probable que necesite encontrar una forma de reescribir su consulta sin usar el
NOT EXISTS
de su consulta.Puede salirse con la suya reescribiendo su consulta usando un
LEFT OUTER JOIN
y comprobando que no había una fila en una tabla pequeña probandoNULL
Probablemente también podría usar una
EXCEPT
consulta, dependiendo de cuántos campos necesite comparar de esta manera:Eso sí, Aaron Bertrand tiene una publicación en el blog que explica los motivos por los que prefiere NO EXISTE, que debe leer para ver si otros enfoques funcionan mejor y para estar al tanto de los posibles problemas de corrección en caso de valores NULL.
Preguntas y respuestas relacionadas: SI EXISTE tarda más que la instrucción select incorporada
fuente
Debe volver a escribir su consulta utilizando combinaciones explícitas y especificar qué operación de combinación desea usar (bucle, hash o fusión) de esta manera.
Cuando se utiliza EXISTS o NOT EXISTS, el plan de consulta generado por SQL Server con operación NESTED LOOP supone que debe pasar por todas las filas del conjunto una por una buscando la primera fila para satisfacer la condición. Usar HASH JOIN lo acelerará.
fuente
Me he encontrado con el mismo problema, me las arreglé para trabajar evitando el uso de "EXISTS" y haciendo uso de la función "COUNT ()" y la instrucción "IF ... ELSE".
Para su ejemplo intente lo siguiente:
La razón por la que agrego "+ 1" al conteo es para que pueda usar "> 1" en la condición IF, usando "> 0" o "<> 0" activará la consulta para usar bucles anidados en lugar de HASH Partido. No he investigado por qué eso está sucediendo exactamente, sería interesante descubrir por qué.
¡Espero que ayude!
fuente