天天看点

移除函数调用能有更好的性能问题:解决方案:总结:

原文出自:

<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

我同意第一种方式容易实现并容易阅读,但是对性能提升来说,性能上的提升比代码量更重要。