2012-01-22 21 views
5

在下面的t-sql語句中,會調用dbo.FUNC函數多少次?當對同一個UDF的多個調用都在一個語句中時,會調用多少次?

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 

FUNC2創建WITH 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僅評估一次。

改寫爲

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 

更改計劃略無一不是隻計算一次

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])) 

現在做輕微的改動,查詢

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 

給出與原始結果相反的結果FUNC2被評估兩次,但FUNC1只有一次。

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

您怎麼知道它被調用了兩次? –

+1

該計劃顯示它在Filter表達式中被調用一次,然後下一個運算符是一個再次調用該函數的計算標量,將結果輸出爲列Expr1003,並且該列用於選擇和排序。 –

+1

+很好的例子。我假設非內聯表UDF ​​總是*每提到每行一次評估,除非有一個明顯的快捷方式(例如ORDER BY表達式完全匹配SELECT表達式) – gbn

-2

是的。

優化程序有足夠的知識在運行時將其優化爲相同的calc。

您可以查看執行計劃以查看它。

+0

雖然執行計劃對於CLR函數,遞歸和循環來說看起來並不準確。 –

+0

FUNc是clr啓用func嗎? –

+0

在我的情況下,FUNC是一個CLR UDF –

1

首先,它取決於如果函數是確定性的。

即便如此,它只會用於多行調用。

我相信你的情況會被優化,如果該功能是確定性的。

+0

我的函數是確定性的,儘管我希望一個「我知道肯定」的答案,而不是一個「我相信」的答案。 –

+0

@GabrielMcAdams說到優化器,你不能保證任何東西 - 只要它保留了語義,它就可以做任何事情。這對於標量UDF來說尤其成問題。通常,避免調用大型數據集的標量UDF,或確保僅使用CTE或臨時表調用一次,以確保邏輯操作順序。事實上,即使在相當大的多維度域上,將UDF轉換爲預先計算的查找表有時效率更高。 –

+0

+1如果我創建了函數WITH SCHEMABINDING,它支持這個答案,然後在我的答案的第一個查詢中的過濾器之前移動計算標量。 –

相關問題