0
    select top 10 *, case     
    when datediff(day,DateOfSale, getDate())  > 5 then '5'
        when datediff(day,DateOfSale, getDate())  > 10 then '10'
... (more datediff clauses)
...
...
        else '20'
        end as jack
        from Foo

SQL Server はdatediff、case ステートメント内で関数呼び出しを 1 回評価し、その値をすべてのwhen句に使用するほどスマートですか? または、関数が「n」回呼び出されていますか?ここで、「n」はwhen句の数ですか?

4

3 に答える 3

3

SQL Server が呼び出しを一度評価する方法を理解するのは困難です。呼び出しにはパラメーターとして列があるため、すべての行に対して評価する必要があります。

したがって、あなたの状態は次のように書かれた方が良いです:

when DateOfSale < dateadd(day, -5, getdate()) then '5'

この場合、差はわずかです。日付の計算は安価です。

関数呼び出しが問題になる典型的な例はwhere、日付列にインデックスを持つテーブルの条件です。たとえば、YourTableのインデックスを使用し(dt)ます。このクエリでは、インデックスを使用できます。

select * from YourTable where dt < dateadd(day, -5, getdate())

このクエリはしませんが:

select * from YourTable where datediff(day, DateOfSale, getDate()) > 5
于 2012-12-31T15:05:32.240 に答える
2

非常に多くの回答がインデックスに言及しているのは不可解です。実際、DATEDIFFSARGable ではありませんが、CASE WHEN によって SQL Server のクエリ オプティマイザーがインデックスの使用を考慮しないため (カバーするスキャン可能なパスを見つけようとする以外は)、ここではまったく関係ありません。私が知る限り、インデックス パスに対する DATEDIFF 関連の式の候補は、この質問とはまったく無関係です。

実際、CASE最初の真の述語が見つかると、SQL Server がステートメント内の述語の評価を停止することを示すのは非常に簡単です。

その事実を実証するために、いくつかのサンプル データを作成してみましょう。

CREATE TABLE Diffy (SomeKey INTEGER NOT NULL IDENTITY(1,1), DateOfSale DATE);

DECLARE @ThisOne AS DATE;
SET @ThisONe = '2012-01-01';
WHILE @thisONe < '2013-01-01'
BEGIN
    INSERT INTO Diffy (DateOfSale) VALUES(@ThisOne);
    SET @ThisOne = DateAdd(d, 1, @ThisOne);
END;

SELECTそれでは、元の質問のパターンにしましょう。元の質問はTOP 10句なしで句を指定してORDER BYいるため、実際に返される値はランダムであることに注意してください。しかし、評価を害する句を に追加すると、CASE何が起こっているかがわかります。

SELECT TOP 10 *, CASE 
WHEN datediff(day,DateOfSale, getDate())  > 5 then '5'
WHEN datediff(day,DateOfSale, getDate())  > 10 then '10'
WHEN 1/0  > 1then 'boom'
ELSE '20' END
AS Jack
FROM Diffy;

1/0 > 1を評価した場合、 のようなものが期待されることに注意してください'Divide by zero error encountered.'。ただし、サーバーに対してこのクエリを実行すると、すべての列に「5」が含まれる 10 行が生成されJackます。

TOP 10 を削除すると、確かにいくつかの行が取得され、Divide by zeroエラーが発生します。したがって、SQL Server が CASE ステートメントで早期終了評価を行っていると結論付けることができます。

それに加えて、ドキュメントには次のようにも書かれています。

CASE ステートメントは、その条件を順番に評価し、条件が満たされた最初の条件で停止します。

DATEDIFF()おそらく、この質問は、共通の部分式がすべてのCASEステートメントから引き上げられ、一度計算されてから、各述語のコンテキスト内で評価されるかどうかを尋ねることを意図しています。の出力を観察するとSET SHOWPLAN_TEXT ON、そうではないと結論付けることができると思います。

   |--Compute Scalar(DEFINE:([Expr1004]=CASE WHEN datediff(day,CONVERT_IMPLICIT(datetimeoffset(7),[Scratch3].[dbo].[Diffy].[DateOfSale],0),CONVERT_IMPLICIT(datetimeoffset(3),getdate(),0))>(5) THEN '5'  ELSE CASE WHEN datediff(day,CONVERT_IMPLICIT(datetimeoffset(7),[Scratch3].[dbo].[Diffy].[DateOfSale],0),CONVERT_IMPLICIT(datetimeoffset(3),getdate(),0))>(10) THEN '10' ELSE CASE WHEN (1)/(0)>(1)  THEN 'boom' ELSE '20' END END END))
        |--Index Scan(OBJECT:([Scratch3].[dbo].[Diffy].[DiffyHelper]))

DATEDIFF()そのことから、このクエリの構造は、行ごと、述語ごとに評価されることを意味していると結論付けることができますO(rows * predicates)。これにより、クエリの CPU 負荷がいくらか発生しますが、DATEDIFF()それほど高価はなく、あまり気にする必要はありません。実際にパフォーマンスの問題を引き起こしていることが判明した場合は、クエリから計算を手動で引き上げる方法があります。たとえばDATEDIFF()、比較の日付相対側です。

于 2012-12-31T15:16:36.077 に答える
1

確かに、あなたの場合はそうではありません(式は行ごとに変化するテーブル列の値に基づいています)が、いずれにしても、テーブル列の値でdatediffを実行せず、述語でdateaddを実行します(比較)値なので、クエリは引き続き DateOfSale の既存のインデックスを使用できます...

  select top 10 *, 
      case When DateOfSale < dateadd(day, -20, getDate()) then '20'
           When DateOfSale < dateadd(day, -15, getDate()) then '15'
           When DateOfSale < dateadd(day, -10, getDate()) then '10' 
           When DateOfSale < dateadd(day, -5, getDate()) then '5' 
          else '20' end jack
  from Foo
于 2012-12-31T15:08:01.497 に答える