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

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

原文出自:

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 [email protected]

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 [email protected]

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

总结:

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

时间: 2024-07-31 17:15:21

移除函数调用能有更好的性能的相关文章

同步效率差不多CAS相比synchronized没有获得更高的性能

对于资源竞争较少的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源:而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能. 希望大家对ASP.NET MVC有一个初步的理解,理论性的东西我们不做过多解释,有些地方不理解也没关系,会用就行了,用的多了,用的久了,自然就理解了. ASP.NET Web API 的好用使用过的都知道,没有复杂的配置文件,一个简单的ApiController加上需要

一种更高查询性能的列存储方式MaxMinT 第一部分

简介本文描述了一种列存储方式和对应的查询方法,这种存储方式具有更好的查询性能和更小的存储空间. And查询 本文先用直观的图形方式展示and查询时的方式,这也是算法要解决的问题核心.通常在OLAP数据查询时,需要进行and处理,例如你需要获取 year = 2017 and customer = 13 的数据,这在列存储中实际是对值 year的2017这个列和 customer的13列进行and操作,而这些列一般都使用位图的方式存储.市面上有很多位图的存储方式,比如WAH, EWAH, Conc

《Python Network Programming Cookbook》读书笔记2---复用socket I/O 实现更好的性能

第二章主要在上一章的基础上介绍了以下内容: 1. ForkingMixIn 2. ThreadingMixIn 3. select.select 4. select.epoll 5. Diesel库 ForkingMixIn 和 ThreadingMixIn属于socketserver(python2是SocketServer)模块,该模块能够简化编写web服务器的工作.其包含四种基本的服务器class: TCPServer 使用TCP协议,在服务器和客户端之间建立持续的连续,安全: UDPSe

如何通过扩展手机内存来使手机发挥更好的性能

相信很多朋友的手机都遇到过这样的问题,发热烫手.红灯亮起.强制退出.无法操作--这可怎么办啊愁坏了我的好机友们.我们在上一篇文章< 如何应对手机应用闪退.强退的现象>http://www.morecomtech.com/community/article/jsfa/w2015100005.php中也已经有了初步的探讨.其实,出现这种状况很多时候是因为CPU占用率太高,也有可能是散热不行,但无论是什么原因导致,这都无一不提示我们,应该为手机降低降降负担了.今天,我们就来介绍一下通过扩展手机内存来

代码示例:一些简单技巧优化JavaScript编译器工作详解,让你写出高性能运行的更快JavaScript代码

告诉你一些简单的技巧来优化JavaScript编译器工作,从而让你的JavaScript代码运行的更快.尤其是在你游戏中发现帧率下降或是当垃圾回收器有大量的工作要完成的时候. 单一同态: 当你定义了一个两个参数的函数,编译器会接受你的定义,如果函数参数的类型.个数或者返回值的类型改变编译器的工作会变得艰难.通常情况下,单一同态的数据结构和个数相同的参数会让你的程序会更好的工作. function example(a, b) { // 期望a,b都为数值类型 console.log(++a * +

更轻更快的Vue.js 2.0与其他框架对比(转)

更轻更快的Vue.js 2.0 崭露头角的JavaScript框架Vue.js 2.0版本已经发布,在狂热的JavaScript世界里带来了让人耳目一新的变化. Vue创建者尤雨溪称,Vue 2.0 在性能上有显著的提升,同时保持轻量的文件下载: 渲染层基于一个轻量级的Virtual DOM实现进行了重写,该Virtual DOM实现fork自snabbdom.新的渲染层相比v1带来了巨大的性能提升,也让Vue 2.0成为了最快速的框架之一. 根据1.0到2.0迁移指南,“大约90%的API是相

[Android 之美] 那些你不知道的APK 瘦身,让你的APK更小

[Android 之美] APK 瘦身,减少APK的大小 让你的apk文件尽可能小,应该使移除未使用的代码和资源文件.那么本章节介绍了如何做到让APK更小,性能更好,下载转化率会更高,以及如何指定在构建APK过程中保留或移除的代码和资源,在我们还没有分析APK大小之前,项目中存在一些资源放置处理不当,没有统一的规范,依赖管理不合理,资源重叠,dex方法数过多等问题,导致APK文件比较大,公司要求APK体积大小要优化到3M左右.经过我们的努力终于达到要求,然而我们发现还能再小. Android 之

29个要点帮你更好的完成java代码优化

在Java程序中,性能问题的大部分原因并不在于Java语言,而是在于程序本身.养成好的代码编写习惯非常重要,比如正确地.巧妙地运用java.lang.String类和java.util.Vector类,它能够显著地提高程序的性能.下面我们就来具体地分析一下这方面的问题. 29个要点帮你更好的完成java代码优化 1. 尽量指定类的final修饰符 带有final修饰符的类是不可派生的. 在Java核心API中,有许多应用final的例子,例如java.lang.String.为String类指定

让Python跑得更快

点击关注 异步图书,置顶公众号 每天与你分享 IT好书 技术干货 职场知识 Tips 参与文末话题讨论,即有机会获得异步图书一本. Python很容易学.你之所以阅读本文可能是因为你的代码现在能够正确运行,而你希望它能跑得更快.你可以很轻松地修改代码,反复地实现你的想法,你对这一点很满意.但能够轻松实现和代码跑得够快之间的取舍却是一个世人皆知且令人惋惜的现象.而这个问题其实是可以解决的. 有些人想要让顺序执行的过程跑得更快.有些人需要利用多核架构.集群,或者图形处理单元的优势来解决他们的问题.有