4

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

DECLARE @START_DATE DATETIME
SET @START_DATE = GETDATE()

SELECT * FROM MY_TABLE
WHERE TRANSACTION_DATE_TIME > DATEADD(MINUTE, -1440, @START_DATE)

上記のselectクエリでは、DATEADDの結果を何度も計算しないように、SqlServerはクエリを最適化しますか。または、DATEADDの結果を一時変数に格納するのは私自身の責任ですか?

4

3 に答える 3

10

実行時定数と見なされるSQLServer関数は、一度だけ評価されます。GETDATE()はそのような関数でありDATEADD(..., constant, GETDATE())、実行時定数でもあります。クエリ内に実際の関数呼び出しを残すことで、オプティマイザーに(変数値のスニフではなく)実際に使用される値を確認させ、それに応じてカーディナリティの見積もりを調整して、より良い計画を立てることができます。

また、これを読んでください:不十分なクエリパフォーマンスのトラブルシューティング:カーディナリティ推定中の定数畳み込みと式の評価

@マーティン・スミス

次のクエリを実行できます。

set nocount on;
declare @known int;
select @known = count(*) from sysobjects;
declare @cnt int = @known;
while @cnt = @known
    select @cnt = count(*) from sysobjects where getdate()=getdate()
select @cnt, @known;

私の場合、22秒後に境界ケースに到達し、ループが終了しました。重要なことは、ループが@cnt ゼロで終了したことです。行ごとにを評価するgetdate()と、正しい@knownカウントとは異なる@cntが得られますが、0は得られないことが予想されます。ループが存在するときに@cntがゼロであるという事実は、それぞれgetdate()が1回評価され、次に同じ定数が評価されたことを示しています。値はすべての行のWHEREフィルタリングに使用されました(一致なし)。1つの肯定的な例が定理を証明しないことは承知していますが、このケースは十分に決定的なものだと思います。

于 2011-09-04T18:33:18.033 に答える
8

驚いたことに、インラインで GETDATE() を使用する方が、この種の計算を事前に実行するよりも効率的であるように思われることがわかりました。

DECLARE @sd1 DATETIME, @sd2 DATETIME;
SET @sd1 = GETDATE();

SELECT * FROM dbo.table
WHERE datetime_column > DATEADD(MINUTE, -1440, @sd1)

SELECT * FROM dbo.table
WHERE datetime_column > DATEADD(MINUTE, -1440, GETDATE())

SET @sd2 = DATEADD(MINUTE, -1440, @sd1);

SELECT * FROM dbo.table
WHERE datetime_column > @sd2;

それらの計画を確認すると、中央のクエリは常に最小のコストで出てきます (ただし、常に最小の経過時間ではありません)。もちろん、それはインデックスとデータに依存する可能性があり、あるクエリに基づいて、同じプリエンプティブ最適化が別のクエリで機能するという仮定を立てるべきではありません。私の本能は、インラインで計算を実行せず、代わりに@sd2上記のバリエーションを使用することです...しかし、私は自分の本能を常に信頼することはできず、経験した行動に基づいて一般的な仮定を立てることはできないことを学びました.特定のシナリオ。

于 2011-09-04T18:12:27.590 に答える
3

一度だけ実行されます。実行計画を確認することで再確認できます(「Compute Scalar」->推定実行数== 1)

于 2011-09-04T18:01:01.157 に答える