SI EXISTE tarda más que la instrucción select incorporada


35

Cuando ejecuto el siguiente código, toma 22.5 minutos y hace 106 millones de lecturas. Sin embargo, si ejecuto solo la instrucción de selección interna por sí sola, solo toma 15 segundos y hace 264k lecturas. Como nota al margen, la consulta de selección no devuelve ningún registro.

¿Alguna idea de por qué la IF EXISTSharía funcionar mucho más tiempo y hacer tantas lecturas más? También cambié la declaración select para hacer SELECT TOP 1 [dlc].[id]y la maté después de 2 minutos.

Como arreglo temporal, lo he cambiado para hacer un recuento (*) y asignar ese valor a una variable @cnt. Entonces hace una IF 0 <> @cntdeclaración. Pero pensé EXISTSque sería mejor, porque si había registros devueltos en la instrucción select, dejaría de realizar la exploración / búsqueda una vez que encontrara al menos un registro, mientras count(*)que completará la consulta completa. ¿Qué me estoy perdiendo?

IF EXISTS
   (SELECT [dlc].[ID]
   FROM TableDLC [dlc]
   JOIN TableD [d]
   ON [d].[ID] = [dlc].[ID]
   JOIN TableC [c]
   ON [c].[ID] = [d].[ID2]
   WHERE [c].[Name] <> [dlc].[Name])
BEGIN
   <do something>
END

44
Para evitar el problema del objetivo de la fila, otra idea (no probada, ¡cuidado!) Podría ser probar el inverso - IF NOT EXISTS (...) BEGIN END ELSE BEGIN <do something> END.
Aaron Bertrand

Respuestas:


32

¿Alguna idea de por qué la IF EXISTSharía funcionar mucho más tiempo y hacer tantas lecturas más? También cambié la declaración select para hacer SELECT TOP 1 [dlc].[id]y la maté después de 2 minutos.

Como expliqué en mi respuesta a esta pregunta relacionada:

¿Cómo (y por qué) TOP impacta un plan de ejecución?

El uso EXISTSintroduce un objetivo de fila, donde el optimizador produce un plan de ejecución destinado a ubicar la primera fila rápidamente. Al hacer esto, se supone que los datos se distribuyen uniformemente. Por ejemplo, si las estadísticas muestran que hay 100 coincidencias esperadas en 100,000 filas, se supondrá que tendrá que leer solo 1,000 filas para encontrar la primera coincidencia.

Esto dará como resultado tiempos de ejecución más largos de lo esperado si esta suposición resulta ser defectuosa. Por ejemplo, si SQL Server elige un método de acceso (por ejemplo, exploración no ordenada) que localiza el primer valor coincidente muy tarde en la búsqueda, podría resultar en una exploración casi completa. Por otro lado, si se encuentra una fila coincidente entre las primeras filas, el rendimiento será muy bueno. Este es el riesgo fundamental con los objetivos de la fila: rendimiento inconsistente.

Como arreglo temporal, lo he cambiado para que cuente (*) y asigne ese valor a una variable

Por lo general, es posible reformular la consulta de modo que no se asigne un objetivo de fila. Sin el objetivo de la fila, la consulta aún puede finalizar cuando se encuentra la primera fila coincidente (si se escribe correctamente), pero es probable que la estrategia del plan de ejecución sea diferente (y con suerte, más efectiva). Obviamente, count (*) requerirá leer todas las filas, por lo que no es una alternativa perfecta.

Si está ejecutando SQL Server 2008 R2 o posterior, generalmente también puede usar el indicador de seguimiento 4138 documentado y compatible para obtener un plan de ejecución sin un objetivo de fila. Este indicador también se puede especificar utilizando la sugerencia compatible OPTION (QUERYTRACEON 4138) , aunque tenga en cuenta que requiere permiso de administrador del sistema en tiempo de ejecución , a menos que se use con una guía de plan.

Desafortunadamente

Ninguno de los anteriores es funcional con una IF EXISTSdeclaración condicional. Solo se aplica a DML normal. Se va a trabajar con el suplente SELECT TOP (1)formulación que ha intentado. Eso puede ser mejor que usar COUNT(*), que tiene que contar todas las filas calificadas, como se mencionó anteriormente.

Dicho esto, hay varias formas de expresar este requisito que le permitirán evitar o controlar el objetivo de la fila, mientras finaliza la búsqueda antes de tiempo. Un último ejemplo:

DECLARE @Exists bit;

SELECT @Exists =
    CASE
        WHEN EXISTS
        (
            SELECT [dlc].[ID]
            FROM TableDLC [dlc]
            JOIN TableD [d]
            ON [d].[ID] = [dlc].[ID]
            JOIN TableC [c]
            ON [c].[ID] = [d].[ID2]
            WHERE [c].[Name] <> [dlc].[Name]
        )
        THEN CONVERT(bit, 1)
        ELSE CONVERT(bit, 0)
    END
OPTION (QUERYTRACEON 4138);

IF @Exists = 1
BEGIN
    ...
END;

El ejemplo alternativo que proporcionó se ejecutó en 3.75 minutos y realizó 46 millones de lecturas. Entonces, aunque es más rápido que mi consulta original, creo que en este caso me quedaré con @cnt = count (*) y luego evaluaré la variable. Sobre todo porque el 99% del tiempo que esto se ejecuta no habrá nada en él. Según las respuestas de usted y Rob, parece que solo existe si realmente espera algún tipo de resultado y ese resultado se distribuye de manera uniforme dentro de sus datos.
Chris Woods

3
@ChrisWoods: Dijiste "Especialmente desde el 99% del tiempo que esto se ejecuta no habrá nada en él". Esto prácticamente garantiza que el objetivo de la fila de uno es una mala idea, ya que espera que generalmente NO haya filas y que tenga que escanear todo para encontrar que no hay ninguna. Si no puede agregar un índice inteligente, quédese con COUNT (*).
Ross Presser

25

Debido a que EXISTS solo necesita encontrar una sola fila, utilizará un objetivo de fila de una. Esto puede producir un plan menos que ideal a veces. Si espera que sea así para usted, complete una variable con el resultado de a COUNT(*)y luego pruebe esa variable para ver si es más de 0.

Entonces ... Con un objetivo de fila pequeño, evitará operaciones de bloqueo, como construir tablas hash u ordenar flujos que podrían ser útiles para unir combinaciones, porque se dará cuenta de que seguramente encontrará algo bastante rápido y, por lo tanto, los bucles anidados lo harían. Sería mejor si encontrara algo. Excepto que esto puede hacer un plan que es mucho peor en todo el conjunto. Si encontrar una sola fila fuera rápido, le gustaría este método para evitar bloques ...

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.