本文出处: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即为内联用户定义函数,当然后面要与表值用户定义函数作比较,就能看出来区别了

  1. create function dbo.fn_InlineFunction
  2. (
  3. @p_parameter varchar(500)
  4. )
  5. returns table
  6. as
  7. return
  8. (
  9. SELECT id,col2
  10. FROM [dbo].[TestTableValueFunction]
  11. where ( col2 = @p_id or @p_id is null)
  12. )
  13. GO

表值用户定义函数(Table-Valued User-Defined Functions),
  与内联函数区别在于,表值用户定义函数返回的是一个表变量,在函数体中,通过赋值给这个表变量,然后返回表变量
  如下dbo.fn_TableValuedFunction即为内联用户定义函数,

  1. create function fn_TableValuedFunction
  2. (
  3. @p_paramter varchar(500)
  4. )
  5. RETURNS @Result TABLE
  6. (
  7. id int ,
  8. value char(5000)
  9. )
  10. as
  11. begin
  12.  
  13. insert into @Result
  14. select id,col2
  15. from [dbo].[TestTableValueFunction]
  16. where ( col2 = @p_id or @p_id is null)
  17.  
  18. return
  19. end

  熟悉sqlserver的同学可能已经知道这两者的区别了,关于内联用户定义函数和表值用户定义函数就先这么简单说一下区别
  虽然内联函数和表值函数在功能上和使用上是有一些差异的,但是有一部分查询,用两种方式都可以实现,也就说两者在功能上有差异也有交集。

开始本文主题

同样的SQL语句,使用内联函数和使用表值函数查询生成执行计划的区别

  按照惯例,先造一个测试表,char(500)的字段可以是的表以及索引占用空间变大,后面对比测试的效果变得更加明显。

  1. create table TestTableValueFunction
  2. (
  3. id int IDENTITY(1,1),
  4. col2 char(500)
  5. )
  6. GO
  7.  
  8. INSERT INTO TestTableValueFunction VALUES (NEWID())
  9. GO 1000000
  10.  
  11. CREATE INDEX idx_col2 on TestTableValueFunction(col2)
  12. 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重编译那点事,内联函数和表值函数在编译生成执行计划的区别的更多相关文章

  1. 关于T-SQL重编译那点事,WITH RECOMPILE和OPTION(RECOMPILE)区别仅仅是存储过程级重编译和SQL语句级重编译吗

    本文出处:http://www.cnblogs.com/wy123/p/6262800.html   在考虑重编译T-SQL(或者存储过程)的时候,有两种方式可以实现强制重编译(前提是忽略导致重编译的 ...

  2. PCB MS SQL 标量函数与表值函数(CLR) 实现文件与目录操作

    一.C#写SQL SERVER(CLR)实现文件操作 标量函数: 文件移动 ,复制,检测文件存在,写入新文件文本,读取文本,创建目录,删除目录,检测目录是否存在 /// <summary> ...

  3. sql标量函数与表值函数

    标量函数 ),)) returns int as begin return (select UserID from UserInfo where UserName=@UserName and User ...

  4. WITH RECOMPILE和OPTION(RECOMPILE)区别仅仅是存储过程级重编译和SQL语句级重编译吗

    在考虑重编译T-SQL(或者存储过程)的时候,有两种方式可以实现强制重编译(前提是忽略导致重编译的其他因素的情况下,比如重建索引,更新统计信息等等), 一是基于WITH RECOMPILE的存储过程级 ...

  5. SQL Server中参数化SQL写法遇到parameter sniff ,导致不合理执行计划重用的一种解决方案

    parameter sniff问题是重用其他参数生成的执行计划,导致当前参数采用该执行计划非最优化的现象.想必熟悉数据的同学都应该知道,产生parameter sniff最典型的问题就是使用了参数化的 ...

  6. SQL 编译与重编译

    编译的含义 当SQLSERVER收到任何一个指令,包括查询(query).批处理(batch).存储过程.触发器(trigger) .预编译指令(prepared statement)和动态SQL语句 ...

  7. sqlserver 存储过程中使用临时表到底会不会导致重编译

    曾经在网络上看到过一种说法,SqlServer的存储过程中使用临时表,会导致重编译,以至于执行计划无法重用, 运行时候会导致重编译的这么一个说法,自己私底下去做测试的时候,根据profile的跟踪结果 ...

  8. SQLSERVER编译与重编译

    SQLSERVER编译与重编译 编译的含义 当SQLSERVER收到任何一个指令,包括查询(query).批处理(batch).存储过程.触发器(trigger) .预编译指令(prepared st ...

  9. 浅析SqlServer简单参数化模式下对sql语句自动参数化处理以及执行计划重用

    我们知道,SqlServer执行sql语句的时候,有一步是对sql进行编译以生成执行计划, 在生成执行计划之前会去缓存中查找执行计划 如果执行计划缓存中有对应的执行计划缓存,那么SqlServer就会 ...

随机推荐

  1. 1.Repeater控件

    在用到数据库数据并且要逐条显示时,就需要用到repeater\listview等这样的数据库控件进行动态的显示数据. Repeater相当于foreach的功能,用于对绑定数据源中的数据进行遍历显示, ...

  2. RPM常见用法

    rpm常见的用法: 命令 说明 rpm -i <.rpm file name> 安装指定的 .rpm 文件 rpm -U <.rpm file name> 用指定的.rpm文件 ...

  3. WordPress Duplicator 0.4.4 Cross Site Scripting

    测试方法: 提供程序(方法)可能带有攻击性,仅供安全研究与教学之用,风险自负! Advisory ID: HTB23162 Product:DuplicatorWordPressPlugin Vend ...

  4. 【高精度】NCPC 2014 C catalansqure

    题目链接: http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1789 题目大意: 求大卡特兰数..公式如下.输入n求Sn(n<=5000) 题目 ...

  5. Android USB安全调试

    Android 4.2.2 引入了USB安全调试方面的内容,当启用安全调试的时候,只有被用户认证过的主机才可以通过Android SDK自带的ADB工具经由USB连接来访问设备的内部构件. 下面以an ...

  6. Evaluate Reverse Polish Notation——LeetCode

    Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +, -, ...

  7. bzoj 2245 [SDOI2011]工作安排(最小费用最大流)

    2245: [SDOI2011]工作安排 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1197  Solved: 580[Submit][Statu ...

  8. 转:VS2010解决方案转换到VS2008

    原文链接地址:http://www.codeproject.com/Tips/80953/Converting-VS2010-Solution-to-VS2008 如果你使用VS2010的任何版本写代 ...

  9. Eucalyptus安装包的功能列表

    aoetools    是一个用来在以太网上运行 ATA 存储协议的软件,相当于一个网络存储功能.euca2ools  eucalpytus客户端杜昂管理工具axis2c       SOAP引擎,同 ...

  10. OpenReports操作指南

    最近要玩OpenReports,但在网上找了一圈,能用的资料少得可怜,所以把最近使用下来,积累的一些技巧记录下来(有部分整合了网上的资源). 备注:以下操作说明基于已做汉化的项目(汉化方案请参考:ht ...