¿SQL Server evalúa las funciones una vez para cada fila?

9

Tengo una consulta como esta:

SELECT col1
FROM   MyTable
WHERE  
    DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) 
       BETWEEN col2 
       AND     col3
;

Esto proporciona información sobre el plan de ejecución similar a esto:

Información sobre herramientas de ejecución

¿Se dateaddejecuta la parte de los predicados de búsqueda para cada fila de la consulta? ¿O SQL Server calcula el valor una vez para toda la consulta?

Stuart Blackler
fuente

Respuestas:

13

Ciertas funciones que se sabe que son constantes de tiempo de ejecución pasan por el proceso llamado plegado constante . Al 'plegar' una constante, se evalúa una expresión al principio de la ejecución de la consulta, el resultado se almacena en caché y el resultado en caché en su lugar cuando es necesario. La expresión en su consulta DATEADD(dd, 0, DATEDIFF(dd, 0, getdate()))es, afaik, una constante de tiempo de ejecución y, por lo tanto, se doblará y evaluará solo una vez por consulta.

Como curiosidad: la RAND()función que uno esperaría que sea desplegable es realmente plegable, lo que conduce a un comportamiento inesperado. Pero otros, por ejemplo NEWID(), no son plegables y forzarán una evaluación por fila.

Remus Rusanu
fuente
2
@StuartBlackler: aquí hay una demostración de cómo funcionan los pliegues de SQL Server GETDATE().
Nick Chammas
2

Los planes de ejecución son geniales, pero a veces simplemente no te dicen la verdad. Así que aquí hay una prueba basada en la prueba de rendimiento.

(y el resultado final: la expresión no se evalúa para cada fila)


;with t(i) as (select 0 union all select i+1 from t where i < 9)
select getdate()-1 as col1,getdate() as col2,getdate() as col3 
into #t 
from t t0,t t1,t t2,t t3,t t4,t t5,t t6,t t7

(100000000 filas afectadas)

Esta es la consulta OP y tarda unos 12 segundos en ejecutarse

SELECT col1
FROM   #t
WHERE  
    DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) 
       BETWEEN col2 
       AND     col3
;

Esta consulta que almacena la fecha en un parámetro antes de la ejecución, toma aproximadamente el mismo tiempo, 12 segundos.

declare @dt datetime = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) 

SELECT col1
FROM   #t
WHERE  
      @dt
       BETWEEN col2 
       AND     col3
;

Y solo para verificar los resultados:
esta consulta que realiza el cálculo en col1 y, por lo tanto, tiene que volver a calcular la expresión para cada fila, tarda aproximadamente 30 segundos en ejecutarse.

SELECT col1
FROM   #t
WHERE  
    DATEADD(dd, 0, DATEDIFF(dd, 0, col1)) 
       BETWEEN col2 
       AND     col3
;

Todas las consultas se ejecutaron repetidamente mostrando las mismas métricas

David דודו Markovitz
fuente