2012-01-22 12 views
5

次のt-sql文では、dbo.FUNC関数が何回呼び出されますか?同じUDFへの複数の呼び出しが1つのステートメント内にある場合、呼び出される回数は何回ですか?

SELECT 
    column1, 
    column2, 
    dbo.FUNC(column3) AS column3 
FROM table1 
WHERE dbo.FUNC(column3) >= 5 
ORDER BY dbo.FUNC(column3) DESC 

は、それは、行ごとに複数の別々の回と呼ばれるのでしょうか、オプティマイザは、単一のステートメントで複数回使用されていることを認識し、一度だけ、それを呼び出すのですか?

どうすればテストできますか?私は関数の内部のテーブルに挿入することはできませんので、カウンターをインクリメントすることはできません...

答えて

11

これは保証されません。

実行計画を確認する必要があります。いくつかの例。

CREATE FUNCTION dbo.FUNC1(@p1 int) 
RETURNS int 
AS 
BEGIN 
    RETURN @p1 + 1 
END 

GO 

CREATE FUNCTION dbo.FUNC2(@p1 int) 
RETURNS int 
WITH SCHEMABINDING 
AS 
BEGIN 
    RETURN @p1 + 1 
END 

GO 
SELECT 
     OBJECTPROPERTYEX(OBJECT_ID('dbo.FUNC1'), 'IsDeterministic'), 
     OBJECTPROPERTYEX(OBJECT_ID('dbo.FUNC2'), 'IsDeterministic') 
GO 

FUNC2WITH SCHEMABINDINGを作成し、決定論的なものとして扱われます。 FUNC1はありません。

SELECT 
    dbo.FUNC1(number) AS FUNC1, 
    dbo.FUNC2(number) AS FUNC2 
FROM master..spt_values 
WHERE dbo.FUNC1(number) >= 5 AND dbo.FUNC2(number) >= 5 
ORDER BY dbo.FUNC1(number), dbo.FUNC2(number) 

は、二回(投影と順序の両方に使用算出列を出力一度フィルタに一度計算スカラーで)評価されるプラン

PLAN1

|--Sort(ORDER BY:([Expr1003] ASC, [Expr1004] ASC)) 
     |--Compute Scalar(DEFINE:([Expr1003]=[test].[dbo].[FUNC1]([master].[dbo].[spt_values].[number]))) 
      |--Filter(WHERE:([test].[dbo].[FUNC1]([master].[dbo].[spt_values].[number])>=(5) AND [Expr1004]>=(5))) 
       |--Compute Scalar(DEFINE:([Expr1004]=[test].[dbo].[FUNC2]([master].[dbo].[spt_values].[number]))) 
         |--Index Scan(OBJECT:([master].[dbo].[spt_values].[ix2_spt_values_nu_nc])) 

FUNC1を与えますFUNC2は1回のみ評価されます。

SELECT 
    FUNC1, 
    FUNC2 
FROM master..spt_values 
CROSS APPLY (SELECT dbo.FUNC1(number), dbo.FUNC2(number)) C(FUNC1, FUNC2) 
WHERE FUNC1 >= 5 AND FUNC2 >= 5 
ORDER BY FUNC1, FUNC2 

として書き換え

は少し計画を変更し、両方のは一度だけ

今クエリ

SELECT 
    FUNC1 + 10, 
    FUNC2 + 10 
FROM master..spt_values 
CROSS APPLY (SELECT dbo.FUNC1(number), dbo.FUNC2(number)) C(FUNC1, FUNC2) 
WHERE FUNC1 >= 5 AND FUNC2 >= 5 
ORDER BY FUNC1, FUNC2 
に若干の変更を行うこと

Plan 2

|--Sort(ORDER BY:([Expr1003] ASC, [Expr1004] ASC)) 
     |--Filter(WHERE:([Expr1003]>=(5))) 
      |--Compute Scalar(DEFINE:([Expr1003]=[test].[dbo].[FUNC1]([master].[dbo].[spt_values].[number]))) 
       |--Filter(WHERE:([Expr1004]>=(5))) 
         |--Compute Scalar(DEFINE:([Expr1004]=[test].[dbo].[FUNC2]([master].[dbo].[spt_values].[number]))) 
          |--Index Scan(OBJECT:([master].[dbo].[spt_values].[ix2_spt_values_nu_nc])) 

評価されます3210

FUNC2が2回評価されますが、FUNC1が1回だけ評価されるという元の結果とは逆の結果になります。

Plan 3

|--Compute Scalar(DEFINE:([Expr1005]=[Expr1003]+(10))) 
     |--Sort(ORDER BY:([Expr1003] ASC, [Expr1004] ASC)) 
      |--Filter(WHERE:([Expr1003]>=(5))) 
       |--Compute Scalar(DEFINE:([Expr1003]=[test].[dbo].[FUNC1]([master].[dbo].[spt_values].[number]))) 
         |--Filter(WHERE:([Expr1004]>=(5))) 
          |--Compute Scalar(DEFINE:([Expr1004]=[test].[dbo].[FUNC2]([master].[dbo].[spt_values].[number]), [Expr1006]=[test].[dbo].[FUNC2]([master].[dbo].[spt_values].[number])+(10))) 
           |--Index Scan(OBJECT:([master].[dbo].[spt_values].[ix2_spt_values_nu_nc])) 
+1

2回呼び出されたことをどのように知っていますか? –

+1

プランはフィルタ式で一度呼び出され、次に演算子が関数を再度呼び出し、結果を列 'Expr1003'として出力し、その列がselectとorderの両方で使用されることを示す。 –

+1

+良い例です。私は非インライン・テーブルUDFは、明示的なショートカットがない限り(例:ORDER BYの式がSELECT式と完全に一致しない限り)、1つの行につき1つの行につき1回評価されると仮定します。 – gbn

-2

はい。

オプティマイザは、実行中にこれを同じ計算に最適化するのに十分な知識があります。

あなたはそれを見るために実行計画を見ることができます。

+0

CLR関数、再帰、ループに関しては、実行計画が正確ではないようです。 –

+0

FUNCはclr enable funcですか? –

+0

私のケースでは、FUNCはCLR UDF –

1

関数が決定的である場合には第一に、それが依存します。

それでも、それは単一の行で複数の呼び出しにのみ使用されます。

私は、関数が決定的である場合は、あなたの場合は、最適化されるだろうと考えています。

+0

私の関数は決定的です。 「私は確かに知っている」答えであり、「私は信じている」答えではない。 –

+0

@GabrielMcAdamsオプティマイザについて言えば、何も保証することはできません。セマンティクスを保持している限り、何でもできます。これはスカラーUDFで特に問題となります。一般に、大規模データ・セットを介して呼び出されるスカラーUDFを回避するか、またはCTEまたは一時表を使用して操作の論理的順序を確実に1回だけ呼び出すようにします。事実、かなり大きなマルチディメンション・ドメインであっても、UDFをあらかじめ計算されたルックアップ・テーブルに変換する方がずっと効率的です。 –

+0

+1この答えをサポートするには、 'WITH SCHEMABINDING'関数を作成すると、答えの最初のクエリのフィルターの前に計算スカラーを移動します。 –

関連する問題