SQL Server使用变量和参数以及语句执行时执行计划的差异

T-SQL语句之间传输数据有以下一些途径:
1)本地变量
2)存储过程中的参数
3)应用程序变量
4)参数标记
上面4种变量中,比较常用的是本地变量和存储过程中的参数。

本地变量指在查询前declare参数,并且set设值之后,在查询语句中直接使用声明的参数,而不是直接使用其值。

存储过程中的参数有两种,一种是定义在存储过程里面,类似上面的本地变量,一种是定义在存储过程外面。这两种参数,都是在执行存储过程时,将参数值传入进去。不过两种定义方法各有优缺点,下一章将进行深入探讨。

应用程序变量主要指编程语言C,C++,Basic和Java等,它们都有自己的一套变量来存储值。应用程序一般通过数据库的API把T-SQL语句返回的值存储到自己的变量中,然后再使用这些值。

参数标记是放置在 T-SQL 语句中的输入表达式位置的一个问号 (?)。参数标记绑定了应用程序变量,似的应用程序变量中的数据可以用作T-SQL语句中的输入。参数标记还允许存储过程输出参数和返回代码绑定到应用程序变量。

本专题主要介绍本地变量和存储过程中的参数的定义,使用以及语句执行时执行计划的差异。

注: 下面每种情况执行之前都执行DBCC FREEPROCCACHE清除了缓存,生产环境避免执行该语句。

不使用变量和参数

直接在SSMS中赋值,SQL Server在编译时知道具体的值,给出的执行计划很准确。
SELECT p.LastName, p.FirstName, ph.PhoneNumber
FROM Person.Person AS p
JOIN Person.PersonPhone AS ph ON p.BusinessEntityID = ph.BusinessEntityID
WHERE LastName LIKE 'Man%';

开启SQL Server Profiler抓取SP:CacheHit和SP:CacheMiss事件,可以发现where条件换个数据后,SQL Server会根据当前的值重新生成执行计划。

使用本地变量

首先我们来看看本地变量的定义及使用方法。
/* Also allowed:
DECLARE @find varchar(30) = 'Man%';
*/
DECLARE @find varchar(30);
SET @find = 'Man%';
SELECT p.LastName, p.FirstName, ph.PhoneNumber
FROM Person.Person AS p
JOIN Person.PersonPhone AS ph ON p.BusinessEntityID = ph.BusinessEntityID
WHERE LastName LIKE @find;
使用本地变量,SQL Server在执行语句时,根据变量的预估值进行编译,给出的执行计划比较准确。

抓取SQL Server Profiler,会发现使用本地变量每次执行都会重新编译,生成新的执行计划。

将变量定义在存储过程里面

示例:
--local variable
create procedure sniff(@find varchar(30)) as
DECLARE @findIn varchar(30);
set @findIn=@find
SELECT p.LastName, p.FirstName, ph.PhoneNumber
FROM Person.Person AS p
JOIN Person.PersonPhone AS ph ON p.BusinessEntityID = ph.BusinessEntityID
WHERE LastName LIKE @findIn;
go
与本地变量一样,它的值在存储过程语句执行过程中得到,SQL Server在运行时不知道变量的值,会根据一个预估值进行编译,给出一个折中的执行计划。
exec sniff 'Man%';

exec sniff 'U%';

将变量定义在存储过程外面

示例:
create procedure sniff2(@find varchar(30)) as
SELECT p.LastName, p.FirstName, ph.PhoneNumber
FROM Person.Person AS p
JOIN Person.PersonPhone AS ph ON p.BusinessEntityID = ph.BusinessEntityID
WHERE LastName LIKE @find;
go
当调用存储过程时,必须要给他带入值。第一次运行存储过程时,SQL Server根据带入的变量值进行编译,给出准确的执行计划。以后再次调用存储过程,SQL Server会重用第一次的执行计划,不会进行重编译,以达到较高的效率,这是存储过程的一大优势。
exec sniff2 'Man%';


exec sniff2 'U%';

不少客户在SQL语句中使用本地变量,会出现同一语句有时快有时慢的情况,从上面的测试应该可以得到答案了。使用存储过程,最大的好处就是可以重用执行计划,大大提高执行效率,但是这一优势同时也是存储过程的最大劣势,首次编译的执行计划如果不适用于新的参数值,有可能会让语句执行时间反很多倍,这就是著名的parameter sniffing,下一章我们将继续深入探讨。

时间: 2024-08-27 09:02:10

SQL Server使用变量和参数以及语句执行时执行计划的差异的相关文章

SQL Server 表变量和临时表系列之概念篇

问题引入 "菜鸟啊,最近我看到阿里云开发者论坛的数据库RDS中有人在提SQL Server表变量和临时表如何选择的问题,你去深入探讨下这个问题吧,解答解答他们的疑惑吧",老鸟又开始为菜鸟找活干了. "鸟哥啊,关于表变量和临时表使用选择的问题啊,向来行业里争论不休,我比较担心我们的观点被人家拍砖啊". "鸟啊,有争论才说明这个问题有价值啊,所以我们才更应该去弄清楚,道明白啊".反正老鸟总会找到合适的理由. "那好吧,要把这个问题要刨根问底

c#-C#程序调用SQL SERVER存储过程的output参数始终为空,求助?

问题描述 C#程序调用SQL SERVER存储过程的output参数始终为空,求助? C#中的如下两条语句调用存储过程output参数始终为空(dataGridView1能正确调用到想要的select数据,但是存储过程的两个输出参数值不能传递给两个texBox.Text),不知为何?textBox1.Text = parameters[2].Value.ToString();textBox2.Text = nowpage + ""/"" + parameters[3

SQL Server 2005 中的批编译、重新编译和计划缓存问题(3)

两种特殊情况 与计划最优性相关的重新编译在下列两种特殊情况中的处理方式有所不同. 特殊情况 1:在空表或索引视图上创建的统计 SQL Server 2005 处理下列情况的方式不同于 SQL Server 2000.用户创建了一个空表 T.然后又在 T 一个或多个列上创建了一个统计 S.由于 T 为空,因此统计二进制大对象(直方图)为 NULL,但已经在 T 上创建了统计.假设在查询编译期间已发现 S 是"令人关注的".根据重新编译阈值的"500 行"规则,只有至少

SQL Server 2005 中的批编译、重新编译和计划缓存问题(2)

查询计划缓存及各种 SET 选项(与 showplan 相关及其他) 各种 SET 选项--多数与 showplan 相关--以多种复杂的方式影响着查询计划和执行上下文的编译.缓存和重用.下表汇总了相关的详细信息. 应按如下顺序阅读该表中的内容.批处理通过表中第一列所指定的特定模式提交给 SQL Server.已提交的批处理的计划缓存中可能存在.也可能不存在已缓存的查询计划.第 2 列和第 3 列描述了存在已缓存的查询计划时的情况:第 4 列和第 5 列说明了不存在已缓存的查询计划时的情况.在每

SQL Server 2005 中的批编译、重新编译和计划缓存问题(1)

本文阐述了批处理在 SQL Server 2005 中的缓存和重用方式,并就最大限度重用已缓存的计划提供了相应的最佳实务.另外,文中还说明了一些重新编译批处理的方案,并针对减少或消除不必要的重新编译,给出了最佳实务. 一.本白皮书的目的 此白皮书的目的有几个.阐述了批处理在 SQL Server 2005 中的缓存和重用方式,并就最大限度重用已缓存的计划提供了相应的最佳实务.另外,文中还说明了一些重新编译批处理的方案,并针对减少或消除不必要的重新编译,给出了最佳实务.本白皮书阐述了 SQL Se

SQL Server 存储过程遇到“表 ''#TT'' 没有标识属性无法执行 SET 操作”错误_MsSql

创建临时表,往临时表插入数据的时候报的错误. 一开始提示没有打开主键,后来打开主键就提示上述错误异常. 从网上查找资料没有找到,然后又到群里问各位大牛,一位大牛告诉我是没有设置主键. 我又仔细看看提示,恍然大悟,我就给临时设置了主键. 没有主键之前的代码: CREATE TABLE #TT( CourseId INT , UserId INT, ClassId INT ) INSERT INTO #TT ( CourseId, UserId, ClassId ) SELECT CourseId,

SQL Server 存储过程遇到“表 ''#TT'' 没有标识属性无法执行 SET 操作”错误

创建临时表,往临时表插入数据的时候报的错误. 一开始提示没有打开主键,后来打开主键就提示上述错误异常. 从网上查找资料没有找到,然后又到群里问各位大牛,一位大牛告诉我是没有设置主键. 我又仔细看看提示,恍然大悟,我就给临时设置了主键. 没有主键之前的代码: CREATE TABLE #TT( CourseId INT , UserId INT, ClassId INT ) INSERT INTO #TT ( CourseId, UserId, ClassId ) SELECT CourseId,

SQL Server 2008下轻松调试T-SQL语句和存储过程

今天突然有同事问起,如何在sqlserver中调试存储过程(我们公司使用的是sqlserver 2008 R2),猛地一看,和以前使用sqlserver 2000真的有很大的不同,我真晕了. 于是琢磨了一下.SQLSERVER 2005中不知因何去掉了很重要的DEBUGGER功能,要调试,必须要安装VS2005专业版或者更高版本.非常不方便. 还好,SQLSERVER 2008中这个很重要而且方便的功能又回来了.     不过,SQLSERVER 2008的调试功能和SQL2000的方法差别很大

SQL Server 表变量和临时表的区别(详细补充篇)_MsSql

一.表变量 表变量在SQL Server 2000中首次被引入.表变量的具体定义包括列定义,列名,数据类型和约束.而在表变量中可以使用的约束包括主键约束,唯一约束,NULL约束和CHECK约束(外键约束不能在表变量中使用).定义表变量的语句是和正常使用Create Table定义表语句的子集.只是表变量通过DECLARE @local_variable语句进行定义. 表变量的特征: 1.表变量拥有特定作用域(在当前批处理语句中,但不在任何当前批处理语句调用的存储过程和函数中),表变量在批处理结束