关于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就会 ...
随机推荐
- codeforces Minesweeper 1D
题意:就是挖地雷,给你一个字符串,‘*’代表地雷,‘1’代表在它的周围有1个地雷,‘2’代表在左右都有个地雷,‘?’代表不确定是不是地雷,可以是1,2,*,问你最后有几种方式确定所有的的地雷. 思路: ...
- Android Wear开发 - 数据通讯 - 第三节 : 事件处理
http://developer.android.com/training/wearables/data-layer/events.html 以下是本人在学习官方开发文档时的笔记,主要是翻译为主,并在 ...
- maven jetty
父项目: <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons- ...
- C语言基础课程 第一课 Linux环境配置小实战httpserver
网段我需要改成如下 10.重启网络服务 并且查看ip 11. 打开windows的浏览器数人Linux的IP地址出现REDHAT的欢迎界面 11.进入目录 12.编写一个简单的html脚本 13 ...
- 矩阵(快速幂):COGS 963. [NOI2012] 随机数生成器
963. [NOI2012] 随机数生成器 ★★ 输入文件:randoma.in 输出文件:randoma.out 简单对比 时间限制:1 s 内存限制:128 MB [问题描述] 栋 ...
- 汇编学习笔记(3)[bx]和loop
本文是<汇编语言>一书的学习笔记,对应书中的4-6章. 汇编程序的执行 要想将源代码变为可执行的程序需经过编译.连接两个步骤,WIN7操作系统下需要MASM程序来进行编译连接工作.将MAS ...
- 简单约瑟夫环的循环单链表实现(C++)
刚刚接触C++以及数据结构,今天做了第一次尝试用C++和数据结构解决问题,问题是基于约瑟夫环问题的简单版. 先来看看约瑟夫环问题的介绍: 约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3.. ...
- SoupUI学习资料
官网: https://www.soapui.org 下载地址: https://www.soapui.org/downloads/soapui.html 官方文档: https://www.soap ...
- 详解udev
如果你使用Linux比较长时间了,那你就知道,在对待设备文件这块,Linux改变了几次策略.在Linux早期,设备文件仅仅是是一些带有适当的属性集的普通文件,它由mknod命令创建,文件存放在/dev ...
- GitLib
http://www.360doc.com/content/15/0603/14/21631240_475362133.shtml 原文 http://blog.csdn.net/williamwan ...