sp_executesql的执行计划会被重用(转载)
前一段时间,给一位朋友公司做咨询,看到他们的很多的存储过程都存在动态sql语句执行,sp_executesql,即使在没有动态表名,动态字段名的情况下仍然使用sp_executesql,这个做法是不太明智的,会存在一些性能方面的问题。
先说说什么场景使用这个系统存储过程吧,sp_executesql,是sql server动态执行一段可以带有参数(内参,外参)的语句文本的系统存储过程,传入sp_executesql 的参数会以参数的形式传递,不会是以拼凑sql的形式传递,所以能够在不得不拼接sql语句的情景下使用以防止sql注入。不得不拼接sql的情景包括 传递in内参数,动态决定表列,列名,还有就是like,为防止sql注入,也不得不拼接sql。按理来说这是一个非常好的存储过程,但是,由于他本身的限制,会对查询性能有很大的影响,下面我举个例子。
使用northwind数据库,
执行:
select * from orders where customerid = 'SAVEA';

执行:
select * from orders where customerid = 'CENTC';

这两个语句的唯一不同就是客户号不一样,一个在订单表内有31个重复值,一个没有重复值。
然后咱们再来对比当这个语句放在了一个动态执行的sql语句内部的情况如何。
创建如下存储过程:
USE [Northwind]
GO
/****** 对象: StoredProcedure [dbo].[testexecutesql] 脚本日期: 10/15/2009 20:09:45 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER proc [dbo].[testexecutesql](@customerid nchar(5))
as
begin exec sp_executesql N'select * from orders where customerid = @cid ',
N'@cid as nchar(5)',@cid = @customerid ;
end
然后执行这个存储过程:
exec testexecutesql 'SAVEA';
其执行计划如下图,是个聚集索引扫描:

表 'Orders'。扫描计数 1,逻辑读取 22 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
使用聚集索引扫描是个很明智的选择,咱们可以来看看customerid上的非聚集索引的统计信息,orders表共830行,其中客户'SAVEA'就有31个订单,所以优化器选择使用聚集索引扫描而不是嵌套循环的book mark look up。
然后咱们再来执行一个:
exec testexecutesql 'CENTC';
区别仅仅是传入的customerid参数不一样,再看看执行计划,仍然是一样,io也是一样,就是返回的行数只有一行,按理来说,只返回一行,优化器应该会选择使用非聚集索引,嵌套查找数据,但是优化器却没有好好利用customerid上的统计信息,仍然使用了聚集索引扫描,为什们?难道是索引上的统计信息不及时吗?不,在手动使用fullscan后的统计信息仍然是一样的查询计划,为什么呢?
因为sp_executesql本身就是一个存储过程,他执行动态语句的参数是不会被利用上的,所以当第一次编译的时候产生的计划,存储过程testexecutesql 是无法嗅探到的,即无法去引用customerid上的统计信息来做查询计划参考的,所以第一次编译的查询计划是聚集索引扫描就是扫描,即使第二次执行的时候应该是查找。
如何才能改变这一现状呢?
可以使用提示符,recompile强制让存储过程在执行的时候重新编译,来获得最好的执行计划,不过这也是有代价的,就是每次都需要编译,不过相比那些被浪费掉的IO,对一些大表的性能低下的查询计划还是很值得的。于是,我们把存储过程改写如下:
USE [Northwind]
GO
/****** 对象: StoredProcedure [dbo].[testexecutesql] 脚本日期: 10/15/2009 20:09:45 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER proc [dbo].[testexecutesql](@customerid nchar(5))
as
begin exec sp_executesql N'select * from orders where customerid = @cid option(recompile) ',
N'@cid as nchar(5)',@cid = @customerid ; --也可以用sp_executesql N'select * from orders where customerid = @cid',N'@cid as nchar(5)',@cid = @customerid with recompile end
这样再次执行
exec testexecutesql 'CENTC';
exec testexecutesql 'SAVEA';
都能获得一个最优的查询计划。
sql server能够支持语句级的重编译,自动嗅探重编译环境,阀值,使得绝大部分情况下能够很好的利用编译后的查询计划,提高数据库整体性能。
所以我们知道了sp_executesql这个系统存储过程默认情况下会保存所执行动态sql语句的执行计划,以便复用。但是用exec(@sql)这种方式执行动态sql,其执行sql语句的执行计划是不会被保存的,每次执行exec(@sql)都是生成一个新的执行计划,所以从原则上来说通过sp_executesql执行动态sql语句的确要比用exec(@sql)性能更高,但是正因如此有时候sp_executesql也会带来一些不必要的麻烦,就如本文所述。
exec(@sql)和sp_executesql @sql有什么不同?
exec(@sql)是每次生成执行计划
sp_executesql 在后面加上with recompile 应该也是每次生成执行计划
这样sp_executesql 的作用仅限在了强制参数话,而无性能上的提升了。
所以这个with recompile的取与用,还需要实际环境做判断。如果加上option(recompile)选项,那么就sp_executesql的执行计划就没有得到充分利用,也就是和EXEC(sql)一样了,只不过稍微比EXEC安全,不会被SQL注入。EXEC和sp_executersql的最大的区别就是sp_executesql能够充分利用执行计划,当然还有sp_executesql支持带参数返回等。。
sp_executesql的执行计划会被重用(转载)的更多相关文章
- Sql server在使用sp_executesql @sql执行文本sql时,报错: Could not find database ID 16, name '16'. The database may be offline. Wait a few minutes and try again.
最近在公司项目中使用exec sp_executesql @sql执行一段文本sql的时候老是报错: Could not find database ID 16, name '16'. The dat ...
- SQL Server中参数化SQL写法遇到parameter sniff ,导致不合理执行计划重用的一种解决方案
parameter sniff问题是重用其他参数生成的执行计划,导致当前参数采用该执行计划非最优化的现象.想必熟悉数据的同学都应该知道,产生parameter sniff最典型的问题就是使用了参数化的 ...
- 浅析SqlServer简单参数化模式下对sql语句自动参数化处理以及执行计划重用
我们知道,SqlServer执行sql语句的时候,有一步是对sql进行编译以生成执行计划, 在生成执行计划之前会去缓存中查找执行计划 如果执行计划缓存中有对应的执行计划缓存,那么SqlServer就会 ...
- SQL Server 利用Profiler观察执行计划是否重用时SP:Cachemiss,SP:CacheInsert以及SP:CacheHit的含义
本文出处:http://www.cnblogs.com/wy123/p/6913055.html 执行计划的缓存与重用 在通过SQL Profile观察一个SQL语句或者存储过程是否有可用的缓存执行计 ...
- 记一次SqlServer大表查询语句优化和执行计划分析
数据库: sqlserver2008r2 表: device_data 数据量:2000w行左右 表结构 CREATE TABLE [dbo].[device_data]( [Id] [int] ID ...
- 使用exec和sp_executesql动态执行SQL语句(转载)
当需要根据外部输入的参数来决定要执行的SQL语句时,常常需要动态来构造SQL查询语句,个人觉得用得比较多的地方就是分页存储过程和执行搜索查询的SQL语句.一个比较通用的分页存储过程,可能需要传入表名, ...
- 【转载】SQL执行计划
要理解执行计划,怎么也得先理解,那各种各样的名词吧.鉴于自己还不是很了解.本文打算作为只写懂的,不懂的懂了才写. 在开头要先说明,第一次看执行计划要注意,SQL Server的执行计划是从右向左看的. ...
- MongoDB干货系列2-MongoDB执行计划分析详解(2)(转载)
写在之前的话 作为近年最为火热的文档型数据库,MongoDB受到了越来越多人的关注,但是由于国内的MongoDB相关技术分享屈指可数,不少朋友向我抱怨无从下手. <MongoDB干货系列> ...
- oracle执行计划(转载)
转载自 https://www.cnblogs.com/Dreamer-1/p/6076440.html 一:什么是Oracle执行计划? 执行计划是一条查询语句在Oracle中的执行过程或访问路径的 ...
随机推荐
- sublime text2 操作及插件
sublime text2 1. 文件快速导航: 这是sublime上面很好用的功能之一,ctrl+p可以调出窗口,菜单上的解释是gotoanythings ,确实如其所言,调出窗口后,直接输入关键字 ...
- Hashtable,HashMap,Dictionary的区别
Hashtable和HashMap的区别:1.Hashtable是基于Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现,c#中无HashMap2.Hashtable ...
- 解决.Net设置只读、隐藏后后台获取不到值的问题
在前台页面上放了几个textbox,用 ReadOnly=true设置不可编辑,用visible="False"设置不可见 用jquery给textbox赋值后在后台页面获取不到t ...
- 为 UIButton 添加长按事件
UIButton *aBtn=[UIButtonbuttonWithType:UIButtonTypeCustom]; [aBtn setFrame:CGRectMake(40, 100, 60, 6 ...
- Oracle直方图导致SQL不走索引.
在ITPUB 上看到一个帖子 http://www.itpub.net/thread-1875212-1-1.html 同一条SQL语句,只有查询条件不一样,查询返回的结果集都为0,一个走了全表扫描, ...
- Java遇见HTML——JSP篇之JSP内置对象(下)
一.什么是session 1.session表示客户端与服务器的一次会话2.Web中的session指:用户在浏览某个网站时,从进入网站到浏览器关闭所经过的这段时间,也就是用户浏览这个网站所花费的时间 ...
- 2、JavaScript常用互动方法
一.输出内容(document.write) document.write() 可用于直接向 HTML 输出流写内容.简单的说就是直接在网页中输出内容. 第一种:输出内容用“”括起,直接输出" ...
- Java ConcurrentHashMap
通过分析Hashtable就知道,synchronized是针对整张Hash表的,即每次锁住整张表让线程独占, ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术. ...
- 【转载】:【C++跨平台系列】解决STL的max()与numeric_limits::max()和VC6 min/max 宏冲突问题
http://www.cnblogs.com/cvbnm/articles/1947743.html 多年以前,Microsoft 幹了一件比 #define N 3 還要蠢的蠢事,那就是在 < ...
- javascript设计模式学习之十五——装饰者模式
一.装饰者模式定义 装饰者模式可以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象.这种为对象动态添加职责的方式就称为装饰者模式.装饰者对象和它所装饰的对象拥有一致的接口,对于用 ...