你可能不知道的技术细节:存储过程参数传递的影响
2016-12-20 16:39
295 查看
前言
很多人认为数据库其实很简单,也没什么大深入的细节去研究,但是真正的一些细节问题决定着你的是否是专家。本文主要讲述一下存储过程参数传递的一些小细节,很多人知道参数嗅探,本例也可以理解成参数嗅探的威力加强版++
小例子
---创建测试表 SELECT IDENTITY(INT,1,1) AS RID, * INTO TB1 FROM sys.all_columns GO ---模拟大量数据 INSERT INTO TB1 SELECT * FROM sys.all_columns GO 100 --在 user_type_id列 创建一个索引 CREATE NONCLUSTERED INDEX [NonClusteredIndex-20160625-164531] ON [dbo].[TB1] ( [user_type_id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] GO --开启IO统计 set statistics io on --测试查询执行计划 select * from tb1 where user_type_id = 10
![](http://www.cnblogs.com/double-k/p/file:/C:/Users/Administrator/AppData/Local/YNote/data/wy3333770800@163.com/8e344306782d46ddb4d51bde8927b833/clipboard.png)
![](http://www.cnblogs.com/double-k/p/file:/C:/Users/Administrator/AppData/Local/YNote/data/wy3333770800@163.com/fd3c4831f9194a5dbb3c5ef03da4c28c/clipboard.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/5598dbb5f754bf304703b29eec08ae75.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/587a1756cae55fe4a90f45129aa4a81a.png)
注:本例中,语句的执行应该走索引seek + key look up
测试一
--测试1:使用定义变量,把参数值传递给变量 create PROCEDURE dbo.USP_GetData ( @PID INT ) AS BEGIN DECLARE @ID INT SET @ID= @PID SELECT * FROM TB1 WHERE user_type_id = @ID END GO EXEC dbo.USP_GetData @PID=10
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/11cf5f3aa229c9827846c951140cadea.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/6d85391d9572a9ed452a8e8b9f63d93b.png)
结论:如果在存储过程中定义变量,并为变量SET赋值,该变量的值无法为执行计划提供参考(即执行计划不考虑该变量),将会出现预估行数和实际行数相差过大导致执行计划不优的情况
测试二
---测试2 : 对参数进行运算 create PROCEDURE dbo.USP_GetData2 ( @PID INT ) AS BEGIN SET @PID=@PID-1 SELECT* FROM TB1 WHERE user_type_id = @PID END GO EXEC dbo.USP_GetData2 @PID=11
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/dd9d4e5ff9c52b48934a5c87d89037f7.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/fd93f5c7c38600b20093b72f61e98733.png)
结论:如果在存储过程中使用SET为存储过程参数重新赋值,执行计划仍采用执行时传入的值来生成执行计划。
测试三
--测试3 :对参数行进拼接 create PROCEDURE dbo.USP_GetData3 ( @PID INT ) AS BEGIN DECLARE @ID INT set @ID = 2 SET @PID = @ID + @PID SELECT * FROM TB1 WHERE user_type_id = @PID END GO EXEC dbo.USP_GetData3 @PID= 8
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/a34b038e178932ab6d8b63e6c218812e.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/157e1de572f54f71198b92aa2c5b7e23.png)
结论:如果在存储过程中使用新定义的变量与传入参数拼接重新赋值,执行计划仍采用执行时传入的值来生成执行计划。
测试四
--测试4 : 对变量进行运算 create PROCEDURE dbo.USP_GetData4 ( @PID INT ) AS BEGIN SELECT * FROM TB1 WHERE user_type_id = @PID+ 2 END GO EXEC dbo.USP_GetData4 @PID=8
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/af9b7d88c959e4748cfba6ace15381ff.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/499036e8aa87958babe65a88c3d25ccb.png)
结论:虽然传入参数在传入后被修改,但是生成执行计划时仍使用传入时的值
测试五
--测试5 :对变量进行复杂运算 create PROCEDURE dbo.USP_GetData5 ( @PID INT ) AS BEGIN SELECT * FROM TB1 WHERE user_type_id = @PID+ CAST(RAND()*600 AS INT) END GO EXEC dbo.USP_GetData5 @PID=8 GO
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/1571f0806a7d550fc94475c2fe4d65cb.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/a9cdbda27250a4d6a060d0c229dcc048.png)
结论:对参数做复杂运算,无法获得准确的值,因此不能准确地预估行数,也不能生成合理的执行计划
测试六
--测试6 : 复杂运算使用变量拼接 create PROCEDURE dbo.USP_GetData6 ( @PID INT ) AS BEGIN DECLARE @ID INT set @ID = CAST(RAND()*600 AS INT) SET @PID = @ID + @PID SELECT * FROM TB1 WHERE user_type_id = @PID END GO EXEC dbo.USP_GetData6 @PID=8 GO
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/15878da294076f7bc9bc1814727d0e48.png)
![](https://oscdn.geek-share.com/Uploads/Images/Content/201612/ca3aa70bbd87f9089b330b11a7869c30.png)
结论:针对测试五可以使用参数拼接的方式,以便准确地预估行数,使用正确的执行计划
总结
技术支持做了比较长的时间了,遇到了很多很多坑,在这些坑中不断反思,慢慢成长!不要说什么数据库更优秀,不要说我们海量数据库需要什么什么高端的技术,其实解决问题的关键只是那么一点点的基础知识。注:本例中还有另外一种情况就是查询的数据量很大,那么本身走全表扫描是最优计划,而由于参数传递的问题错误的走了index seek + key look up 道理是一样的。
--------------博客地址-----------------------------------------------------------------------------
原文地址: http://www.cnblogs.com/double-K/
如有转载请保留原文地址!
----------------------------------------------------------------------------------------------------
注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错请点击下右下角的推荐,非常感谢!
相关文章推荐
- SQL Server--你可能不知道的技术细节:存储过程参数传递的影响
- sqlserver存储过程的参数传递注意事项
- oracle 存储过程详细介绍(创建,删除存储过程,参数传递等)
- SQLServer-存储过程中使用字符串和分隔符实现传递数组参数(转)
- 在存储过程中,用xml作为参数传递
- oracle 创建,删除存储过程,参数传递,创建,删除存储函数,存储过程和函数的查看,包,系统包
- Sql 存储过程 参数传递空字符串, 到SQL 端变成了 空格!!
- SQL server 存储过程中解决整数参数的传递问题
- 网易视频云技术专家分析:你可能不知道的MYSQL“隐藏”参数
- SQLServer-存储过程中使用字符串和分隔符实现传递数组参数
- ReportView如何使用构造SQL语句带参数的存储过程创建报表以及为rdlc传递参数
- C# 调用sql的存储过程并传递参数
- 解决SQL中in参数在存储过程中的传递问题
- 分页存储过程,可将查询条件作为参数传递的分页查询存储过程
- SQL实现递归及存储过程中In()参数传递解决方案详解
- SQL中in参数在存储过程中传递及使用的方法
- Oracle带输入、输出参数的存储过程 返回受影响的行数 SQL%ROWCOUNT
- 分页存储过程,可将查询条件作为参数传递的分页查询存储过程
- Oracle带输入、输出参数的存储过程 返回受影响的行数 SQL%ROWCOUNT
- Mysql向存储过程中传递中文参数变成乱码的解决方案