2011-12-02 117 views
5

使用MSSQL 2005UDF VS直接SQL性能

我今天玩弄一個標量UDF在WHERE語句看到一些與進行呼叫相關的成本和IO的差異等

我的m從2個基本表開始。擁有100萬行的客戶。和購買有10萬。兩者都有一個自動標識列作爲主鍵。沒有定義其他索引。

DBCC FREEPROCCACHE 
DBCC DROPCLEANBUFFERS 

SET STATISTICS IO ON 
    SELECT * FROM Customer C 
    INNER JOIN Purchases P on C.[IDENTITY] = P.CustomerID 
    WHERE P.Amount > 1000 
SET STATISTICS IO OFF 

這返回的

Table 'Customer'. Scan count 0, logical reads 3295, physical reads 1, read-ahead reads 32, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Purchases'. Scan count 1, logical reads 373, physical reads 1, read-ahead reads 370, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 

IO統計於是正好看到一個標量UDF的話,我剛搬來的P.Amount> 1000至UDF的影響。功能如下:

CREATE FUNCTION [dbo].[HighValuePurchase] 
(
    @value int 
) 
RETURNS bit 
AS 
BEGIN 
    DECLARE @highValue bit 
    SET @highValue = '0' 

    IF @value > 1000 
    BEGIN 
     SET @highValue = '1' 
    END 
    RETURN @highValue 
END 

所以我然後跑下面的查詢:

DBCC FREEPROCCACHE 
DBCC DROPCLEANBUFFERS 

SET STATISTICS IO ON  
    SELECT * FROM Customer C 
    INNER JOIN Purchases P on C.[IDENTITY] = P.CustomerID 
    WHERE dbo.HighValuePurchase(P.Amount) = '1' 
SET STATISTICS IO OFF 

我期待這個運行雪上加霜。此查詢返回以下IO統計信息:

Table 'Purchases'. Scan count 1, logical reads 373, physical reads 1, read-ahead reads 370, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Customer'. Scan count 1, logical reads 35, physical reads 3, read-ahead reads 472, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 

這也返回比> 1000查詢更快的速度。當返回相同的行時,調用UDF的那個行的順序被C自動排序。[IDENTITY]其中另一個查詢出現未排序。這可能是由於合併在執行計劃中完成的方式。計劃大綱如下。

非UDF的執行計劃顯示採購的集羣索引掃描和在嵌套連接處合併的客戶的集羣索引查找。

UDF版本的執行計劃顯示採購的聚集索引掃描,然後是過濾器,然後是排序。客戶有一個聚集索引掃描。然後將結果合併到合併聯接中。

我相信這與缺乏索引等有關,但我不確定爲什麼這些結果是他們的方式。我體驗過UDF的運行速度很慢,每個人都說使用它們通常是一個壞主意,這就是爲什麼我把這個測試放在一起的原因。目前我無法解釋爲什麼UDF版本看起來好多了。

+0

找到了一些與此相關的信息:http://sqlinthewild.co.za/index.php/2009/04/29/functions-io-statistics-and-the-execution-plan/ 我仍然可以' t獲得上述查詢的「真實」IO統計數據,但這至少有助於解釋它。 – Equixor

+0

看起來你不會在這個論壇上得到答案。可能要發佈在更多的SQL特定網站上。即。 http://www.sqlservercentral.com/Forums/ –

+0

爲什麼你會命名一列IDENTITY ???爲什麼不把它命名爲兩個表中的CustomerID?命名這樣的事情只會殺死下一個可憐的人,在你之後必須在這個系統上工作。 –

回答

1
  • 如果你想加入Purchases.CustomerID你應該把它放在一個索引。
  • 如果您經常查詢數值範圍,您應該在其中加上索引。

因爲你要求SQL服務器在兩個不好的計劃之間進行選擇。

SQL Server可以大致猜出> 1000查詢將涵蓋多少次購買,並將基於此查詢計劃。

但是,它無法猜測UDF查詢將覆蓋多少個,因此可能會選擇不同的計劃。因爲它無知,它可能比其他計劃更好或更差,這取決於它的猜測有多好。

您可以看到生成的計劃,它會告訴您每個計劃的估計行數以及實際數量。這些估計數字在每種情況下都是計劃的選擇。