SQL Serverは行ごとに関数を1回評価しますか?


9

次のようなクエリがあります。

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

これにより、次のような実行プランのツールチップが表示されます。

実行ツールチップ

dateadd求める述語の一部は、クエリ内のすべての行に対して実行されますか?または、SQL Serverはクエリ全体の値を1回計算しますか?

回答:


13

ランタイム定数として知られている特定の関数は、定数フォールディングと呼ばれるプロセスを通過します。定数を「折りたたむ」ことにより、式はクエリ実行の早い段階で評価され、結果はキャッシュされ、必要に応じてキャッシュされた結果がキャッシュされます。クエリの式はDATEADD(dd, 0, DATEDIFF(dd, 0, getdate()))ランタイム定数なので、クエリごとに1回だけ折りたたまれ、評価されます。

雑学として:展開RAND()可能であると期待される関数は実際には折りたたみ可能であり、予期しない動作が発生します。しかし、たとえばNEWID()、その他は折りたたみ可能ではなく、行ごとに評価を強制します。


2
@StuartBlackler- SQL Serverがどのように機能するかを示すデモ次に示しますGETDATE()
Nick Chammas

2

実行計画は素晴らしいですが、時には真実を伝えないこともあります。したがって、ここにパフォーマンステストに基づく証明があります。

(そして一番下の行-式はすべての行に対して評価されていません)


;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行が影響を受けました)

これはOPクエリであり、実行に約12秒かかります

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

実行前にパラメータに日付を保存するこのクエリは、ほぼ同じ時間、12秒かかります。

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

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

そして結果を確認するだけ
です-col1で計算を行うこのクエリは、すべての行の式を再計算する必要があるため、実行に約30秒かかります。

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

すべてのクエリが繰り返し実行され、ほぼ同じメトリックが表示されました

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.