天天看點

移除函數調用能有更好的性能問題:解決方案:總結:

原文出自:

<a href="http://www.mssqltips.com/sqlservertip/2727/removing-function-calls-for-better-performance-in-sql-server/?utm_source=dailynewsletter&amp;utm_medium=email&amp;utm_content=headline&amp;utm_campaign=2012726">http://www.mssqltips.com/sqlservertip/2727/removing-function-calls-for-better-performance-in-sql-server/?utm_source=dailynewsletter&amp;utm_medium=email&amp;utm_content=headline&amp;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

我同意第一種方式容易實作并容易閱讀,但是對性能提升來說,性能上的提升比代碼量更重要。