`
sjk2013
  • 浏览: 2183507 次
文章分类
社区版块
存档分类
最新评论

移除函数调用能有更好的性能

 
阅读更多

原文出自:

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

问题:

大部分人都知道不要在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]

(@CarSaleID INT)

RETURNS VARCHAR (500)

AS

BEGIN

RETURN (SELECT Top 1coalesce(LastName,CompanyName)

FROM Buyer

WHERE CarSaleID=@CarSaleID

ORDER BY BuyerID)

END

GO

原始查询:

SELECT cs.PurchaseDate,
 dbo.fnGetBuyerFirstName(cs.CarSaleID),
 dbo.fnGetBuyerLastName(cs.CarSaleID)
FROM CarSale cs
ORDER BY CarSaleID;

从上面代码中可以看出,每条记录都调用一次函数。并且查询了Buyer表两次。当CarSale表有大量数据时,这种做法并不高效。执行计划如下:


即使我们使用where子句限制查询并只查询一条数据,通过查看执行计划,如下,可以看到,依旧要对Buyer表做两次搜索。


修改后的查询:

SELECT cs.PurchaseDate,
 dbo.fnGetBuyerFirstName(cs.CarSaleID),
 dbo.fnGetBuyerLastName(cs.CarSaleID)
FROM CarSale cs
WHERE CarSaleID=5
ORDER BY CarSaleID;

值得注意的是,在这个例子中,只返回了一条记录。一下带有更广where条件从而返回更多数据的查询会变得越来越慢。

去除函数的例子:

现在移除select中的函数调用,并使用表关联来实现同样结果,其中一个是使用了where子句,另外一个没有限制:

SELECT cs.PurchaseDate,FirstName,LastName
FROM CarSale cs 
INNER JOIN (SELECT CarSaleID,MIN(BuyerID) AS SingleBuyerID FROM Buyer GROUP BY CarSaleID) m2 
ON cs.CarSaleID=m2.CarSaleID
INNER JOIN Buyer m ON m2.CarSaleID=cs.CarSaleID AND m2.SingleBuyerID=m.BuyerID
ORDER BY cs.CarSaleID;

SELECT cs.PurchaseDate,FirstName,LastName
FROM CarSale cs 
INNER JOIN (SELECT CarSaleID,MIN(BuyerID) AS SingleBuyerID FROM Buyer GROUP BY CarSaleID) m2 
ON cs.CarSaleID=m2.CarSaleID
INNER JOIN Buyer m ON m2.CarSaleID=cs.CarSaleID AND m2.SingleBuyerID=m.BuyerID
WHERE cs.CarSaleID=5
ORDER BY cs.CarSaleID;

通过查看执行计划,可以得出不用函数以后,不再需要每条记录都去重新查找。这是通过merge join来处理的。


为了确认这点,我们看看刚才去掉了函数之后的查询,通过sql Profiler的跟踪,可以得到多大的性能提升:

Query

WHERE Clause

CPU (ms)

Reads

Writes

Duration

Original

NO

10734

1239655

0

25879

YES

0

9

0

0

No Function Call

NO

578

16337

0

2457

YES

0

11

0

0

通过上面的结果可以看出,当返回的结果很大时,能从中得到相当大的好处,包括CPU、逻辑读、持续时间等。当只返回一个结果时,性能更好。

最终版本,使用CTE:

因为在本例中,使用函数来返回单独的买家,所以可以使用CTE来取得进一步的性能:

WITH summary AS (SELECT CarSaleID, 
BuyerID, 
FirstName,
 LastName,
 ROW_NUMBER() OVER(PARTITION BY CarSaleID ORDER BY CarSaleID) AS rk 
FROM Buyer) 
SELECT PurchaseDate,s.FirstName,s.LastName FROM CarSale cs INNER JOIN summary s 
ON s.CarSaleID=cs.CarSaleID WHERE s.rk = 1;

WITH summary AS (SELECT CarSaleID, 
BuyerID, 
FirstName,
 LastName,
 ROW_NUMBER() OVER(PARTITION BY CarSaleID ORDER BY CarSaleID) AS rk 
FROM Buyer) 
SELECT PurchaseDate,s.FirstName,s.LastName FROM CarSale cs INNER JOIN summary s 
ON s.CarSaleID=cs.CarSaleID WHERE s.rk = 1 AND cs.CarSaleID=5;

通过执行计划和sqlprofiler对比得到:

Query

WHERE Clause

CPU (ms)

Reads

Writes

Duration

No Function Call add WITH statement

NO

266

15796

0

1931

YES

0

6

0

0

总结:

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

分享到:
评论

相关推荐

    LuaBind 源码 (Lua增强库)

    请注意, (如果你对性能有很高的需求)把你的函数放到表里面将增加查找函数的时间. 7 绑定函数到Lua 为了绑定函数到Lua,你可以使用函数 luabind::def(). 它的声明如下: template, class policies> void def(const ...

    HGE_系列教材(1-9)

    3)技术优势--- 基于Direct3D API 有较好的性能和特性 4)免费--- 对于个人或者商业用户都免费,遵循zlib/libpng license 5)代码高度的一致性--- 代码是否具有一致性,是衡量代码质量的标准之一(《Co de Reading: ...

    jQuery详细教程

    jQuery详细教程,讲解很透彻, 一. jQuery 语法实例 ...不过,把它们放到一个单独的文件中会更好,就像这样(通过 src 属性来引用文件): 实例 <script type="text/javascript" src="jquery.js"></script> ...

    Google C++ 编码规范

    定义(Definition):当函数被声明为内联函数之后,编译器可能会将其内联展开,无需按通常的函数调用机制调用内联函数。 重要的是,虚函数和递归函数即使被声明为内联的也不一定就是内联函数。通常,递归函数不应该...

    中文版Excel.2007高级VBA编程宝典.part1

     11.1.16 在单元格区域中插入值的更好方法  11.1.17 传递一维数组中的内容  11.1.18 将单元格区域传递给Variant类型的数组  11.1.19 按数值选择单元格  11.1.20 复制非连续的单元格区域  11.2 处理工作簿和...

    超级有影响力霸气的Java面试题大全文档

    不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。  接口(interface...

    内存管理内存管理内存管理

    在很多脚本语言中,您不必担心内存是如何管理的,这并不能使得内存管理的重要性有一点点降低。对实际编程来说,理解您的内存管理器的能力与局限性至关重要。在大部分系统语言中,比如 C 和 C++,您必须进行内存...

    javascript入门笔记

    调用:在JS中任何的合法位置处,都可以通过 函数名() 的方式进行调用 练习: 1、声明一个函数,名称为 change 2、在函数中 1、通过弹框,分两次,录入两个数字,保存在 a 和 b 2、先打印 a 和 b的值 3、如果...

    java 面试题 总结

    不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。 接口(interface)...

    Qt Creator 的安装和hello world 程序+其他程序的编写--不是一般的好

    加入的这个函数的作用就是移除字符串开头和结尾的空白字符。 12.最后,如果输入错误了,重新回到登录对话框时,我们希望可以使用户名和 密码框清空并且光标自动跳转到用户名输入框,最终的登录按钮的单击事件的槽 ...

    操作系统(内存管理)

    在很多脚本语言中,您不必担心内存是如何管理的,这并不能使得内存管理的重要性有一点点降低。对实际编程来说,理解您的内存管理器的能力与局限性至关重要。在大部分系统语言中,比如 C 和 C++,您必须进行内存管理...

    ITween插件

    函数以Gizmos结尾的和没有的效果一样,而以Handles结尾的暂时有一个不兼容的问题,不能用,这样算下来,这几个函数里面有用的也就三个: DrawLine() DrawPath() PathLength() 在这里还有一个函数,用于给你的...

    asp.net面试题

    具体说来,由于密封类永远不会有任何派生类,所以对密封类的实例的虚拟函数成员的调用可以转换为非虚拟调用来处理。 8.列举ADO.NET中的五个主要对象,并简单描述 connection,command,dataReader,trans,dataset ......

    超实用的jQuery代码段

    7.1 如何更好地处理图片法显示问题 7.2 如何显示图片直到页面加载完成 7.3 预加载显示图片的方法 7.4 Facebook风格的图片预加载 7.5 检查图片src是否有效 7.6 上下滑动的图片 7.7 淡入淡出一幅图片,进入另一幅图片 ...

    软件框架设计的艺术

    1.5 更好的无绪 12 第2章 设计API的动力之源 14 2.1 分布式开发 14 2.2 模块化应用程序 16 2.3 交流互通才是一切 20 2.4 经验主义编程方式 22 2.5 开发第一个版本通常比较容易 24 第3章 评价...

    易语言-精易模块源码V6.9.0

    3、新增“类_编辑框菜单”类模块,动态在编辑框菜单插入或尾部添加新菜单,更好的扩展编辑框自带的菜单功能; 4、新增“窗口_是否全屏”检测指定窗口是否全屏; 5、新增“文件_创建稀疏文件”“文件_是否是稀疏文件...

    易语言-精易模块V6.9.0

    3、新增“类_编辑框菜单”类模块,动态在编辑框菜单插入或尾部添加新菜单,更好的扩展编辑框自带的菜单功能; 4、新增“窗口_是否全屏”检测指定窗口是否全屏; 5、新增“文件_创建稀疏文件”“文件_是否是稀疏文件...

Global site tag (gtag.js) - Google Analytics