关于T-SQL重编译那点事,内联函数和表值函数在编译生成执行计划的区别
本文出处:http://www.cnblogs.com/wy123/p/6266724.html
最近在学习 WITH RECOMPILE和OPTION(RECOMPILE)在重编译上的区别的时候,无意中发现表值函数和内联表值函数编译生成执行计划的区别
下文中将会对此问题展开讨论。
简单地说就是:同样一句SQL,分别写成内联函数和表值函数,然后执行对Function的查询,发现其执行计划和执行计划缓存是不一样的,
根据某些测试的一些共同规律发现,内联函数的编译很有可能与Parameter Embedding Optimization 有关
关于Parameter Embedding Optimization,我在http://www.cnblogs.com/wy123/p/6262800.html写了一个案例
在发生Parameter Embedding Optimization做编译优化的时候,跟普通的编译优化机制还是有很大差异的。
概念解释:内联用户定义函数和表值用户定义函数
SQL Server中的表值函数分为“内联用户定义函数”和“表值用户定义函数”。
内联用户定义函数(Inline User-Defined Functions):
不上MDSN上搬概念了,简单地说,内联函数的特点就是就是返回类型为table,返回的结果是一个查询语句
如下,dbo.fn_InlineFunction即为内联用户定义函数,当然后面要与表值用户定义函数作比较,就能看出来区别了
create function dbo.fn_InlineFunction
(
@p_parameter varchar(500)
)
returns table
as
return
(
SELECT id,col2
FROM [dbo].[TestTableValueFunction]
where ( col2 = @p_id or @p_id is null)
)
GO
表值用户定义函数(Table-Valued User-Defined Functions),
与内联函数区别在于,表值用户定义函数返回的是一个表变量,在函数体中,通过赋值给这个表变量,然后返回表变量
如下dbo.fn_TableValuedFunction即为内联用户定义函数,
create function fn_TableValuedFunction
(
@p_paramter varchar(500)
)
RETURNS @Result TABLE
(
id int ,
value char(5000)
)
as
begin insert into @Result
select id,col2
from [dbo].[TestTableValueFunction]
where ( col2 = @p_id or @p_id is null) return
end
熟悉sqlserver的同学可能已经知道这两者的区别了,关于内联用户定义函数和表值用户定义函数就先这么简单说一下区别
虽然内联函数和表值函数在功能上和使用上是有一些差异的,但是有一部分查询,用两种方式都可以实现,也就说两者在功能上有差异也有交集。
开始本文主题
同样的SQL语句,使用内联函数和使用表值函数查询生成执行计划的区别
按照惯例,先造一个测试表,char(500)的字段可以是的表以及索引占用空间变大,后面对比测试的效果变得更加明显。
create table TestTableValueFunction
(
id int IDENTITY(1,1),
col2 char(500)
)
GO INSERT INTO TestTableValueFunction VALUES (NEWID())
GO 1000000 CREATE INDEX idx_col2 on TestTableValueFunction(col2)
GO
同样的查询条件下,分别用内联函数和表值函数查询,查看其性能
首先使用内联函数的方式查询,用插入数据中的一条值做查询,最直观的方式去看SSMS的执行时间,显示为0秒,本机测试几乎是瞬间就出来结果了
可以看到执行计划走的是原始表TestTableValueFunction上idx_col2索引查找Index Seek

观察IO,发现发生了8次IO

使用表值函数的方式查询,使用上面同样的条件做查询,SSMS显式耗时4秒(本机测试的,可以忽略测试环境的外界影响因素)
但是使用表值函数无法直接观察查询的执行计划和IO信息,这两个信息后面从计划缓存中查询

其显示的IO信息应该也不是原始的SQL的IO,应该是表变量的IO,原始SQL语句的IO和执行计划信息暂时看不到,后面再说

为什么同样的查询,使用表值函数,性能差异居然有这么大?
对于表值函数,由于无法直接观察到其实际执行计划和IO信息,那么我们去查询其缓存的执行计划和IO信息
如下,sys.dm_exec_query_stats系统表中查询到其最近一次执行的IO信息,76997,远远大于上面的8次IO
查看缓存的执行计划

从缓存中的执行计划可以看到,其执行计划为全表扫描

到这里就有意思了,既然是一样的SQL,写成内联函数和表值函数,两者的执行计划不一样,
那么就可以推断出,SQL Server对内联函数和表值函数的编译处理方式是不一样的。
同时,上面的内联函数是可以知道看到实际执行计划的,显示为Index Seek,
但是在观察缓存计划的时候,是没有查到的,如下截图,也就是说内联函数dbo.fn_InlineFunction对应的SQL的执行计划是没有被缓存起来的
种种迹象不由的使我想到上一篇关于T-SQL重编译那点事中,OPTION(RECOMPILE)的The Parameter Embedding Optimization编译优化机制
从内联函数的SQL的执行计划发现,编译过程中是对SQL语句做植入参数优化+简化,又因为没有缓存执行计划,那么很有可能是发生了重编译
从个这两点来看,跟OPTION(RECOMPILE)强制重编译中的The Parameter Embedding Optimization编译优化机制基本上是吻合的

回头再说表值函数为什么是全表扫描?参考下图,正常情况下这种查询逻辑就是走的全表扫描
只不过是内联函数里面,编译优化机制对这种写法做了专门的优化,才能走一个索引查找的方式。
这也正是内联函数和表值函数在编译上最大的区别之一。
对于为什么表值函数里面这种逻辑会在造成全表扫描在上一篇也解释了,这里就不啰嗦了。

如上,同样的T-SQL查询,在末尾加上OPTION(RECOMPILE),执行计划也变成了Index Seek,跟内联函数的执行计划一致(都是index Seek),当然内联函数中是没有加OPTION(RECOMPILE)的
因此这里有理由怀疑,内联函数的编译,是类似等价于加了OPTION(RECOMPILE)的

之前只是了解过内联函数和表值函数在预估方面的区别(不过记得好像是SQL Server2012之后对表值函数的预估计算方式也做了更新),
除此之外,从来没有注意到也没有考虑过两者在编译以及计划缓存方面的区别
工作中见到过有人使用内联函数做复杂的查询,并且是查询条件是(col2 =@p_parameter or @p_parameter is null)这种方式
如果是在存储过程中,这种方式是会抑制到索引的使用的,之前“理所当然地”认为,写成内联函数,肯定也会抑制索引的使用
不过从这个测试case来看,内联函数这种写法,确实可以正常使用索引
总结
本文通过一个简单的case,来演示了内联函数和表值函数在编译上的一些差别,优化器对内联函数进行专门的优化处理,而不会去对表值函数做特别的优化。
在对内联函数做特殊优化的时候,虽然没有明确执行强制重编译,但等效于存在类似于option(recompile)的基于sql语句级的强制重编译优化机制。
关于T-SQL重编译那点事,内联函数和表值函数在编译生成执行计划的区别的更多相关文章
- 关于T-SQL重编译那点事,WITH RECOMPILE和OPTION(RECOMPILE)区别仅仅是存储过程级重编译和SQL语句级重编译吗
本文出处:http://www.cnblogs.com/wy123/p/6262800.html 在考虑重编译T-SQL(或者存储过程)的时候,有两种方式可以实现强制重编译(前提是忽略导致重编译的 ...
- PCB MS SQL 标量函数与表值函数(CLR) 实现文件与目录操作
一.C#写SQL SERVER(CLR)实现文件操作 标量函数: 文件移动 ,复制,检测文件存在,写入新文件文本,读取文本,创建目录,删除目录,检测目录是否存在 /// <summary> ...
- sql标量函数与表值函数
标量函数 ),)) returns int as begin return (select UserID from UserInfo where UserName=@UserName and User ...
- WITH RECOMPILE和OPTION(RECOMPILE)区别仅仅是存储过程级重编译和SQL语句级重编译吗
在考虑重编译T-SQL(或者存储过程)的时候,有两种方式可以实现强制重编译(前提是忽略导致重编译的其他因素的情况下,比如重建索引,更新统计信息等等), 一是基于WITH RECOMPILE的存储过程级 ...
- SQL Server中参数化SQL写法遇到parameter sniff ,导致不合理执行计划重用的一种解决方案
parameter sniff问题是重用其他参数生成的执行计划,导致当前参数采用该执行计划非最优化的现象.想必熟悉数据的同学都应该知道,产生parameter sniff最典型的问题就是使用了参数化的 ...
- SQL 编译与重编译
编译的含义 当SQLSERVER收到任何一个指令,包括查询(query).批处理(batch).存储过程.触发器(trigger) .预编译指令(prepared statement)和动态SQL语句 ...
- sqlserver 存储过程中使用临时表到底会不会导致重编译
曾经在网络上看到过一种说法,SqlServer的存储过程中使用临时表,会导致重编译,以至于执行计划无法重用, 运行时候会导致重编译的这么一个说法,自己私底下去做测试的时候,根据profile的跟踪结果 ...
- SQLSERVER编译与重编译
SQLSERVER编译与重编译 编译的含义 当SQLSERVER收到任何一个指令,包括查询(query).批处理(batch).存储过程.触发器(trigger) .预编译指令(prepared st ...
- 浅析SqlServer简单参数化模式下对sql语句自动参数化处理以及执行计划重用
我们知道,SqlServer执行sql语句的时候,有一步是对sql进行编译以生成执行计划, 在生成执行计划之前会去缓存中查找执行计划 如果执行计划缓存中有对应的执行计划缓存,那么SqlServer就会 ...
随机推荐
- VS2015开发的Office Addin部署,安装时报错:无法解析属性“type”的值。
用VS2012开发的Outlook插件,在多数情况下安装正常,但是在某些机器上,安装时出现以下错误: 打开VSTOInstaller.exe.config文件查看,其中内容是: <?xml ve ...
- 中文简体windows CMD显示中文乱码解决方案
因为重装系统,以前是英文的,现在的镜像文件是中文简体windows 10.所以只能将就使用. 下载了JDK,CMD 写了命令java,结果一堆乱码(问号???).发现System的locale默认设置 ...
- Vi的几种退出方式
1.q 退出 2.w 保存,继续操作 3.wq 保存退出 4.q! 不保存,放弃修改 5.x 同wq相似,但又有区别 wq 强制性写入文件并退出.即使文件没有被修改也强制写入,并更新文件的修改时间 ...
- Lowest Common Ancestor in Binary Tree
The problem: Given node P and node Q in a binary tree T. Find out the lowest common ancestor of the ...
- BZOJ 1093 [ZJOI2007]最大半连通子图
1093: [ZJOI2007]最大半连通子图 Time Limit: 30 Sec Memory Limit: 162 MBSubmit: 1986 Solved: 802[Submit][St ...
- 将汉字转化为拼音,正则表达式和得到汉字的Unicode编码
一:上图,不清楚的看代码注解,很详细了 二:具体代码 窗体代码 using System; using System.Collections.Generic; using System.Compone ...
- ie中弹出框中元素的定位
用selenium在ie8浏览器中定位一个弹出框时,直接用ie developer tools可能不一定能定位到,有一个解决的办法是直接在url后面加上#noHide,刷新后,然后再用ie devel ...
- 数学概念——I - 数论,线性方程
I - 数论,线性方程 Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit ...
- linux新内核中关闭硬盘的DMA
vortex86 SIS550 Minit-5250E瘦客户机,使用CF卡启动,显示不支持DMA. 搜索得新内核已基本不再使用ide=nodma参数了,查到这篇文章:“Debian下关闭CF卡的DMA ...
- OpenERP新手易犯错误之res.model
接触OpenERP的人都感慨资料之少,尤其是XML中,出点错是相当郁闷的.尤其是新手.什么都别说了,有图有真相. 视图中关联模型name="model" ,而动作中name=&qu ...