原文出自:
<a href="http://www.mssqltips.com/sqlservertip/2727/removing-function-calls-for-better-performance-in-sql-server/?utm_source=dailynewsletter&utm_medium=email&utm_content=headline&utm_campaign=2012726">http://www.mssqltips.com/sqlservertip/2727/removing-function-calls-for-better-performance-in-sql-server/?utm_source=dailynewsletter&utm_medium=email&utm_content=headline&utm_campaign=2012726</a>
大部分人都知道不要在where子句中調用函數,這樣會影響你的性能。但是如果在SELECT中使用呢?本文将嘗試移除select中的函數調用能戲劇性地增強性能,特别在傳回大資料量時。
在此例中,我們将建立兩個示例表和兩個通路這些表的函數。至于填充表,你将不得不使用一種工具,如Visual
Studio來填充他們以提供一些合理的真實資料。在本例中,将對每個表填充20萬資料。其中一件需要注意的事是,這些示例函數隻傳回每個買家的一筆記錄。幾時存在多個買家。
下面是代碼:
-- Table creationlogic
CREATE TABLE[dbo].[CarSale](
[CarSaleID] [int] IDENTITY(1,1) NOT NULL,
[PurchaseDate] [smalldatetime] NOT NULL,
CONSTRAINT [PK_CarSale] PRIMARY KEYCLUSTERED ([CarSaleID] ASC)
);
CREATE TABLE[dbo].[Buyer](
[BuyerID] [int] IDENTITY(1,1) NOT NULL,
[CarSaleID] [int] NOT NULL,
[LastName] [varchar](50) NULL,
[FirstName] [varchar](100) NULL,
[CompanyName] [varchar](200) NULL,
CONSTRAINT [PK_Buyer] PRIMARY KEY NONCLUSTERED([BuyerID] ASC)
ALTER TABLE[dbo].[Buyer] WITH CHECK ADD CONSTRAINT[FK_Buyer_CarSale] FOREIGN KEY([CarSaleID])
REFERENCES[dbo].[CarSale] ([CarSaleID]) ON UPDATE CASCADE ON DELETE CASCADE;
CREATE CLUSTEREDINDEX [IX_Buyer_CarSalelID] ON [dbo].[Buyer]([CarSaleID] ASC);
-- Function creationlogic
CREATE FUNCTION[dbo].[fnGetBuyerFirstName]
(@CarSaleID INT)
RETURNS VARCHAR (500)
AS
BEGIN
RETURN (SELECT Top 1FirstName
FROM Buyer
WHERE CarSaleID=@CarSaleID
ORDER BY BuyerID)
END
GO
CREATE FUNCTION[dbo].[fnGetBuyerLastName]
RETURN (SELECT Top 1coalesce(LastName,CompanyName)
從上面代碼中可以看出,每條記錄都調用一次函數。并且查詢了Buyer表兩次。當CarSale表有大量資料時,這種做法并不高效。執行計劃如下:

即使我們使用where子句限制查詢并隻查詢一條資料,通過檢視執行計劃,如下,可以看到,依舊要對Buyer表做兩次搜尋。
修改後的查詢:
值得注意的是,在這個例子中,隻傳回了一條記錄。一下帶有更廣where條件進而傳回更多資料的查詢會變得越來越慢。
現在移除select中的函數調用,并使用表關聯來實作同樣結果,其中一個是使用了where子句,另外一個沒有限制:
通過檢視執行計劃,可以得出不用函數以後,不再需要每條記錄都去重新查找。這是通過merge join來處理的。
為了确認這點,我們看看剛才去掉了函數之後的查詢,通過sql Profiler的跟蹤,可以得到多大的性能提升:
Query
WHERE Clause
CPU (ms)
Reads
Writes
Duration
Original
NO
10734
1239655
25879
YES
9
No Function Call
578
16337
2457
11
通過上面的結果可以看出,當傳回的結果很大時,能從中得到相當大的好處,包括CPU、邏輯讀、持續時間等。當隻傳回一個結果時,性能更好。
因為在本例中,使用函數來傳回單獨的買家,是以可以使用CTE來取得進一步的性能:
通過執行計劃和sqlprofiler對比得到:
No Function Call add WITH statement
266
15796
1931
6
我同意第一種方式容易實作并容易閱讀,但是對性能提升來說,性能上的提升比代碼量更重要。