Bewertet SQL Server Funktionen einmal für jede Zeile?

9

Ich habe eine Frage wie diese:

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

Dies gibt einen Tooltip zum Ausführungsplan, der dem folgenden ähnelt:

Ausführungs-Tooltip

Wird der dateaddTeil der Suchprädikate für jede Zeile in der Abfrage ausgeführt? Oder berechnet SQL Server den Wert einmal für die gesamte Abfrage?

Stuart Blackler
quelle

Antworten:

13

Bestimmte Funktionen, von denen bekannt ist, dass sie Laufzeitkonstanten sind , durchlaufen den Prozess, der als konstantes Falten bezeichnet wird . Durch "Falten" einer Konstante wird ein Ausdruck zu Beginn der Abfrageausführung ausgewertet, das Ergebnis wird zwischengespeichert und das zwischengespeicherte Ergebnis wird stattdessen bei Bedarf zwischengespeichert. Der Ausdruck in Ihrer Abfrage DATEADD(dd, 0, DATEDIFF(dd, 0, getdate()))ist afaik eine Laufzeitkonstante und wird daher nur einmal pro Abfrage gefaltet und ausgewertet.

Als Trivia: Die RAND()Funktion, von der man erwarten würde, dass sie entfaltbar ist, ist tatsächlich faltbar, was zu unerwartetem Verhalten führt. Andere sind beispielsweise NEWID()nicht faltbar und erzwingen eine Auswertung pro Zeile.

Remus Rusanu
quelle
2
@StuartBlackler - Hier ist eine Demonstration, wie SQL Server-Funktionen funktionieren GETDATE().
Nick Chammas
2

Ausführungspläne sind großartig, aber manchmal sagen sie einfach nicht die Wahrheit. Hier ist also ein Beweis, der auf einem Leistungstest basiert.

(und das Endergebnis - der Ausdruck wird nicht für jede Zeile ausgewertet)


;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 betroffene Zeile)

Dies ist die OP-Abfrage und die Ausführung dauert ca. 12 Sekunden

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

Diese Abfrage, bei der das Datum vor der Ausführung in einem Parameter gespeichert wird, dauert ungefähr 12 Sekunden.

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

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

Und nur um die Ergebnisse zu überprüfen -
Diese Abfrage, die die Berechnung für col1 durchführt und daher den Ausdruck für jede Zeile neu berechnen muss, dauert ungefähr 30 Sekunden.

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

Alle Abfragen wurden wiederholt ausgeführt und zeigten ungefähr dieselben Metriken

David דודו Markovitz
quelle