曾经在网络上看到过一种说法,SqlServer的存储过程中使用临时表,会导致重编译,以至于执行计划无法重用,
运行时候会导致重编译的这么一个说法,自己私底下去做测试的时候,根据profile的跟踪结果,
存储过程中使用临时表,如果不是统计信息变更导致导致的重编译,并不会导致重编译,
但是现实情况下,对于一些特殊的情况,即便是统计信息没有更新,又确实会出现每次运行都重编译的情况,
存储过程中使用了临时表,什么情况下会重编译,什么情况下不用重编译?
为了弄清楚这个问题,查阅了大量的资料,才把这个问题弄清楚,这里特意记录下来,
希望武断地认为存储过程中使用了临时表就会导致重编译的这个观点得到纠正。

首先进行下面的测试,我们知道,导致临时表重编译的因素之一就是统计信息的变化,统计信息的变化依赖于往临时表中写入的数据量,
首选我要控制插入临时表中的数据量不超过统计信息更新而导致重编译的阀值,先排除统计信息的变更导致重编译,
看看仅仅是多次运行SP,是否因为存储过程中有了临时表而会产生重编译

--首选创建一个表,供存储过程中测试使用
create table test1
(
id int identity(1,1),
name varchar(50)
)
--插入10000条测试数据
insert into test1 values (NEWID())
go 10000 --创建一个存储过程,其中存储过程中定义了一个临时表,根据参数,往临时表中写入数据
create proc testRecompile(@i int)
as
begin
create table #t (id int,name varchar(50))
insert into #t select id,name from test1 where id<@i
select * from #t
end

那么就开始运行这个SP,然后监控profile,看看第一次运行,以及除了第一次运行之后,到底有没有发生重编译

--第一次运行,代入参数1
exec testRecompile 1
--第二次运行,代入参数2
exec testRecompile 2

下面是profile的截图,可以很清楚地看到,第一次运行之后,再次运行SP的时候,没有发生重编译的动作,也就是说重用了第一次的执行计划缓存

这里解释两个问题,
1,第一次运行的时候,为什么不是因为架构更改导致的重编译,而是Deferred Compile?
2,第二次运行的时候,为什么没有重编译,因为临时表是每次运行的时候创建的啊,肯定是更改了架构(change schema)了,为什么没有重编译?

首先,说明第一个问题,

1,第一次运行的时候,当存储过程testRecompile编译的时候,
  插入语句(insert into #t select id,name from test1 where id<@i)和查询语句(select * from #t),
  因为#t表还没有被创建,因为这两句并没有被编译,
  编译的时候的执行计划并没有完全完成,
  当这个存储过程执行的时候,临时表才被创建,此时才真正的开始编译临时表对象的语句,这个编译的过程是执行的时候完成的,而不是纯粹的编译阶段完成的
  所以这是Deferred Compile,也即是运行时才进行的编译,就是所谓的延迟编译(Deferred Compile)。
2,第二个问题,重新运行临时表的时候,按道理,因为创建了临时表,必然导致架构的更改,为什么没有重编译?
  这个是因为,存储过程中使用了临时表,对临时表的使用是引用其“名称”(比如这里的#t),而非ID(从临时数据库中查询sys.sysobjects)
  虽然多个会话同时运行这个SP的话,每个会话都会生成一个临时表,每个会话生成的临时表的ID都是不同的,
  但是要注意的是,存储过程中并没有直接使用临时表对象的ID,而是临时表名字本身,
  第一次运行之后,缓存的执行计划与第二次运行时一样的,所以第二次运行这个SP可以重用这个第一次生成的执行计划,

上面说了,在某些情况下,存储过程中使用临时表会导致重编译,这是在什么情况下发生的呢?
因为在某些情况下,要先生成临时表,然后以动态sql的方式去执行一段有临时表参与的sql,此时对于临时表的引用是引用其ID,而不是名称
这个要归结于对于临时表的调用方式,当存储过程中定义了临时表,用sp_executesql的方式调用的时候,这两种执行sql的方式相当于新建了会话,
此时因为不同回话之间,同一个临时表生成的ID是不同的,此时才会导致存储过程中发生sechme change的重编译
上代码

create proc testRecompile2(@i int)
as
begin
create table #t (id int,name varchar(50))
insert into #t select id,name from test1 where id<=@i
exec('select * from #t')
end DBCC FREEPROCCACHE
--第一次运行,代入参数1
exec testRecompile2 1
--第二次运行,代入参数2
exec testRecompile2 2

在存储过程中创建了临时表,执行的时候到底发生不发生重编译,取决于你怎么使用这个临时表
以sp_executesql的方式执行临时表的sql的时候,才会发生因为schema change导致的重编译,
因为这两种方式执行sql,相当于新建会话去执行sql,此时对于临时表的引用,是引用临时表生成的ID,不同会话之间的临时表对象的ID是不同的,所以无法重用执行计划,会发生重编译

另外,对于临时表的另一种导致重编译的因素就是统计信息,对于统计信息变更导致的重编译,就不多说了,这个不仅仅会发生在临时表上,普通的物理表上也会因为统计信息变更导致重编译,不止是临时表,唯一的区别就是,导致临时表与物理表统计信息变更的阀值是不一样的

另外,对于统计信息变更导致的重编译,就不多说了,这个不仅仅会发生在临时表上,普通的物理表上也会因为统计信息变更导致重编译,不止是临时表,唯一的区别就是,导致临时表与物理表统计信息变更的阀值是不一样的

我们知道

这个也很容易验证,临时表统计信息更新的阀值依赖于临时表中数据的变化幅度,这个阀值如下

If n < 6, Recompilation threshold = 6.

If 6 <= n <= 500, Recompilation threshold = 500.

If n > 500, Recompilation threshold = 500 + 0.20 * n.

sqlserver 存储过程中使用临时表到底会不会导致重编译的更多相关文章

  1. Sqlserver 存储过程中结合事务的代码

    Sqlserver 存储过程中结合事务的代码  --方式一 if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[ ...

  2. Sybase:存储过程中采用临时表存储统计数据

    Sybase:存储过程中采用临时表存储统计数据 作用 很有效的提升统计查询速度,对于数据量亿级.千万级多表之间关联查询,非常有效: 使用 --无需定义临时表,直接使用 --自动释放临时表 select ...

  3. 【转载】Sqlserver存储过程中使用Select和Set给变量赋值

    Sqlserver存储过程是时常使用到的一个数据库对象,在存储过程中会使用到Declare来定义存储过程变量,定义的存储过程变量可以通过Set或者Select等关键字方法来进行赋值操作,使用Set对存 ...

  4. 搜索sqlserver 存储过程中的关键字

    搜索sqlserver 存储过程中的关键字 select * from sys.all_sql_modules where definition like '%SP_NAME%'

  5. 在ORACLE存储过程中创建临时表

    在ORACLE存储过程中创建临时表 存储过程里不能直接使用DDL语句,所以只能使用动态SQL语句来执行 --ON COMMIT DELETE ROWS 说明临时表是事务指定,每次提交后ORACLE将截 ...

  6. Mysql存储过程(六)——存储过程中使用临时表

    Mysql 不支持数组.但有时候需要组合几张表的数据,在存储过程中,经过比较复杂的运算获取结果直接输出给调用方,比如符合条件的几张表的某些字段的组合计算. Mysql 临时表可以解决这个问题. 临时表 ...

  7. Oracle存储过程中使用临时表

    一.Oracle临时表知识 在Oracle中,临时表分为SESSION.TRANSACTION两种,SESSION级的临时表数据在整个SESSION都存在,直到结束此次SESSION:而 TRANSA ...

  8. Sqlserver数据库中的临时表详解

    临时表在Sqlserver数据库中,是非常重要的,下面就详细介绍SQL数据库中临时表的特点及其使用,仅供参考. 临时表与永久表相似,但临时表存储在tempdb中,当不再使用时会自动删除.临时表有两种类 ...

  9. SqlServer存储过程中常用函数及操作

    1.case语句 用于选择语句 SELECT ProductNumber, Category = CASE ProductLine WHEN 'R' THEN 'Road' WHEN 'M' THEN ...

随机推荐

  1. Ajax的同步与异步

    原文地址:http://www.cnblogs.com/Joetao/articles/3525007.html <%@ Page Language="C#" AutoEve ...

  2. 《理解 ES6》阅读整理:函数(Functions)(六)Purpose of Functions

    明确函数的双重作用(Clarifying the Dual Purpose of Functions) 在ES5及更早的ES版本中,函数调用时是否使用new会有不同的作用.当使用new时,函数内的th ...

  3. C#更改文件访问权限所有者(适用于各个Windows版本)

    前面也提到了,前段时间在做Online Judge系统,在正式上线前有几个比较老的版本,其中第一个版本使用ACL来控制权限以确保安全(但是这个版本完全建立在IIS上,所以这样做是没效果的),遇到了一些 ...

  4. 【腾讯Bugly干货分享】微信读书iOS性能优化

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/578c93ca9644bd524bfcabe8 “8小时内拼工作,8小时外拼成长 ...

  5. Ng Http Request/response格式转换

    angular作为Single Page Application推荐的交互方式当然是基于json的ajax调用.但今天要说的是当你不幸工作在一个遗留或者不可控制的服务上,而这服务是基于非json提交方 ...

  6. Nim教程【九】

    向关注这个系列的朋友们,道一声:久违了! 它并没有被我阉掉,他一定会得善终的,请各位不要灰心 Set集合类型 为了在特殊场景下提高程序的性能设置了Set类型,同时也是为了保证性能,所以Set只能容纳有 ...

  7. Unity3D热更新全书-脚本(三) C#LightEvil语法与调试

    调试,这是一个无法规避的问题 C#Light 由于有 词法解释.语法解释.运行时三种情况 所以和C#也是有类似的问题 出错大致可以分为编译错误和运行时错误 拼写出莫名的东西或者语法不正确,会在编译阶段 ...

  8. 整理BOM时写的关于拆分单元格的VB代码

    Public Function AddRows(pos As Integer, amount As Integer) Dim rpos As Integer rpos = pos + To amoun ...

  9. 学习Scala02 基本类型

    scala中有9大基本类型: Byte .Short .Int .Long. Char .String .Float. Double .Boolean 与java的基本类型看起来基本是一致的,但实际上 ...

  10. 基于Task的异步模式的定义

    返回该系列目录<基于Task的异步模式--全面介绍> 命名,参数和返回类型 在TAP(Task-based Asynchronous Pattern)中的异步操作的启动和完成是通过一个单独 ...