曾经在网络上看到过一种说法,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. Python path

    1.os.path 1.1.os.path.dirname(__file__) 1.1若文件__file__以绝对路径被运行的,则输出绝对路径 1.2若文件以相对路径被运行的,则输出为空 1.2.os ...

  2. cent7内核升级4.9

    一.手动档 手动档就是从源码开始编译内核安装,好处是可以自己选择任意版本的内核,缺点就是耗时长,编译安装消耗系统资源 1.1.获取 kernel 源码 这世界上最伟大的 Linux 内核源码下载地址是 ...

  3. linux系统用户以及用户组管理

    本系列的博客来自于:http://www.92csz.com/study/linux/ 在此,感谢原作者提供的入门知识 这个系列的博客的目的在于将比较常用的liunx命令从作者的文章中摘录下来,供自己 ...

  4. 使用knockout-sortable实现对自定义菜单的拖拽排序

    在开始之前,照例,我们先看效果和功能实现. 关于自定义菜单的实现,这里就不多说了,需要了解的请访问:http://www.cnblogs.com/codelove/p/4838766.html 这里需 ...

  5. Win10 UWP开发中的重复性静态UI绘制小技巧 2

    小技巧1 地址:http://www.cnblogs.com/ms-uap/p/4641419.html 介绍 我们在上一篇博文中展示了通过Shape.Stroke族属性实现静态重复性UI绘制,使得U ...

  6. 上层建筑——DOM元素的特性与属性(dojo/dom-attr)

    上一篇返本求源中,我们从DOM基础的角度出发,总结了特性与属性的关系.本文中,我们来看看dojo框架是如何处理特性与属性的.dojo框架中特性的处理位于dojo/dom-attr模块属性的处理为与do ...

  7. 使用ACE_Task管理线程

    为什么要使用ACE_Task来管理线程 从C#转到C++后,感觉到C++比C#最难的地方,就是在系统编程时,C#中有对应的类库,我接触到一个类后,就可以通过这个类,知道很多相关的功能.而在C++中,必 ...

  8. Unity3D热更新全书-脚本(一) 初识脚本

    开篇之前还是要先说明,这是一份给经验并不丰富的程序员阅读的文字. 有需求.有疑惑,往下看. 第一个问题什么是脚本?程序和脚本如何区分?我们给Unity编写的组件是程序还是脚本? 这些问题本文无意去解答 ...

  9. HTML5打造的炫酷本地音乐播放器-喵喵Player

    将之前捣腾的音乐频谱效果加上一个播放列表就成了现在的喵喵播放器(Meow meow Player,额知道这名字很二很装萌~),全HTML5打造的网页程序,可本地运行也可以挂服务器上用. 在线Demo及 ...

  10. 细说.NET中的多线程 (六 使用MemoryBarrier,Volatile进行同步)

    上一节介绍了使用信号量进行同步,本节主要介绍一些非阻塞同步的方法.本节主要介绍MemoryBarrier,volatile,Interlocked. MemoryBarriers 本文简单的介绍一下这 ...