Czy SQL Server ocenia funkcje raz dla każdego wiersza?

9

Mam takie zapytanie:

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

Daje to podpowiedź do planu wykonania podobną do tej:

Etykieta wykonania

Czy dateaddczęść predykatów wyszukiwania jest wykonywana dla każdego wiersza w zapytaniu? Czy też SQL Server oblicza wartość raz dla całego zapytania?

Stuart Blackler
źródło

Odpowiedzi:

13

Niektóre funkcje, o których wiadomo, że są stałymi wykonawczymi, przechodzą proces zwany ciągłym składaniem . Poprzez „składanie” stałej, wyrażenie jest oceniane na początku wykonywania zapytania, wynik jest buforowany, a buforowany wynik w razie potrzeby. Wyrażenie w zapytaniu DATEADD(dd, 0, DATEDIFF(dd, 0, getdate()))jest, afaik, stałą czasu wykonywania, a zatem zostanie złożone i ocenione tylko raz na zapytanie.

Jako ciekawostki: RAND()funkcja, której można by się spodziewać, można w rzeczywistości złożyć, co prowadzi do nieoczekiwanego zachowania. Ale inne, na przykład NEWID(), nie są składane i wymuszą ocenę według wiersza.

Remus Rusanu
źródło
2
@StuartBlackler - Oto demonstracja tego, jak SQL Server fałduje funkcje GETDATE().
Nick Chammas
2

Plany egzekucji są świetne, ale czasem nie mówią prawdy. Oto dowód oparty na teście wydajności.

(i dolny wiersz - wyrażenie nie jest oceniane dla każdego wiersza)


;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

(Dotyczy 100000000 wierszy)

To jest zapytanie OP i jego uruchomienie zajmuje około 12 sekund

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

To zapytanie, które przechowuje datę w parametrze przed wykonaniem, trwa mniej więcej w tym samym czasie, 12 sekund.

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

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

I tylko w celu zweryfikowania wyników -
To zapytanie, które wykonuje obliczenia na col1 i dlatego musi ponownie obliczyć wyrażenie dla każdego wiersza, zajmuje około 30 sekund.

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

Wszystkie zapytania były wykonywane wielokrotnie, pokazując mniej więcej te same dane

David Markודו Markovitz
źródło