SQL Server--你可能不知道的技术细节:存储过程参数传递的影响

前言

很多人认为数据库其实很简单,也没什么大深入的细节去研究,但是真正的一些细节问题决定着你的是否是专家。

  本文主要讲述一下存储过程参数传递的一些小细节,很多人知道参数嗅探,本例也可以理解成参数嗅探的威力加强版++

小例子

---创建测试表
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

注:本例中,语句的执行应该走索引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

结论:如果在存储过程中定义变量,并为变量SET赋值,该变量的值无法为执行计划提供参考(即执行计划不考虑该变量),将会出现预估行数和实际行数相差过大导致执行计划不优的情况

测试二

---测试2 : 对参数进行运算
create PROCEDURE dbo.USP_GetData2
(
  @PID INT
)
AS
BEGIN
SET @PID[email protected]1
SELECT*
FROM TB1
WHERE user_type_id = @PID
END
GO
EXEC dbo.USP_GetData2 @PID=11

结论:如果在存储过程中使用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

结论:如果在存储过程中使用新定义的变量与传入参数拼接重新赋值,执行计划仍采用执行时传入的值来生成执行计划。

测试四

--测试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

结论:虽然传入参数在传入后被修改,但是生成执行计划时仍使用传入时的值

测试五

--测试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

结论:对参数做复杂运算,无法获得准确的值,因此不能准确地预估行数,也不能生成合理的执行计划

测试六

--测试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

结论:针对测试五可以使用参数拼接的方式,以便准确地预估行数,使用正确的执行计划

总结

  技术支持做了比较长的时间了,遇到了很多很多坑,在这些坑中不断反思,慢慢成长!不要说什么数据库更优秀,不要说我们海量数据库需要什么什么高端的技术,其实解决问题的关键只是那么一点点的基础知识。

  注:本例中还有另外一种情况就是查询的数据量很大,那么本身走全表扫描是最优计划,而由于参数传递的问题错误的走了index seek + key look up 道理是一样的。

  

--------------博客地址-----------------------------------------------------------------------------

原文地址: http://www.cnblogs.com/double-K/

如有转载请保留原文地址! 

----------------------------------------------------------------------------------------------------

注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接!

时间: 2024-10-17 04:46:09

SQL Server--你可能不知道的技术细节:存储过程参数传递的影响的相关文章

如何在SQL Server查询语句(Select)中检索存储过程(Store Procedure)的结果集?

如何在SQL Server查询语句(Select)中检索存储过程(Store Procedure)的结果集?(2006-12-14 09:25:36) 与这个问题具有相同性质的其他描述还包括:如何在SQL Server存储过程中获取另一存储过程的执行结果记录集?如何在存储过程中检索动态SQL语句的执行结果?如何实现类似SELECT * FROM (EXEC procedure_name @parameters_var) AS datasource ... 的功能?procedure_

sql server 查询某个表被哪些存储过程调用

原文:sql server 查询某个表被哪些存储过程调用 sql server 查询某个表被哪些存储过程调用 select distinct object_name(id) from syscomments where id in (select id from sysobjects where type ='P') and text like'%TableName%' 原文地址:https://www.cnblogs.com/lonelyxmas/p/9491635.html

SQL Server 数据库的维护(上)_存储过程(procedure)

--维护数据库----存储过程(procedure)----概述: SQl Serve的存储过程是由一个或多个T-SQL语句组成的一个集合.常用的程序代码段通常被创建成存储过程,一次创建多次调用,这样既简化程序员的工作也减少与服务器交互的网络通信流量.存储过程中可以包含数据库中执行操作的程序语句,也包括调用其他过程.存储过程可以接收和输出参数,向调用它的程序返回值.存储过程被调用后,会返回给调用它的程序状态值,以表明调用成功或者调用失败以及调用失败的原因.--使用存储过程的优点:1)减少网络流量

SQL SERVER链接服务器执行带参存储过程

SQL SERVER通过链接服务器,链接到ORACLE数据库,下面我要在SQL SERVER数据库上写一个存储过程,该存储过程需要用通过链接服务去取ORACLE数据库里的数据,该存储过程是含参数的存储过程.在SQL SERVER 数据库里创建一个存储过程来取ORACLE数据库里的一个表里的数据如下: 1,在SQL SERVER数据库上创建存储过程 USE [ProdDB] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO -- =======

SQL Server 并行操作优化,避免并行操作被抑制而影响SQL的执行效率

为什么我也要说SQL Server的并行: 这几天园子里写关于SQL Server并行的文章很多,不管怎么样,都让人对并行操作有了更深刻的认识. 我想说的是:尽管并行操作可能(并不是一定)存在这样或者那样的问题,但是我们不能否认并行,仍然要利用好并行. 但是,实际开发中,某些SQL语句的写法会导致用不到并行,从而影响到SQL的执行效率 所以,本文要表达的是:我们要利用好并行,不要让一些SQL的写法问题“抑制”了并行,让我们享受不了并行带来的快感 关于SQL Server的并行: 所谓的并行,指S

SQL Server事务回滚对自增键的影响

SQL Server事务回滚时是删除原先插入导致的自增值,也就是回滚之前你你插入一条数据导致自增键加1,回滚之后还是加1的状态 --如果获取当前操作最后插入的identity列的值:select @@IDENTITY--如果要获取某表的最后的identity列的值:select IDENT_CURRENT('表名') --如果要模拟抛出异常可以用RAISERROR --RAISERROR('错误的描述',错误的严重级别代码,错误的标识,错误的描述中的参数的值(这个可以是多个),一些其它参数) -

sql server 带输入输出参数的分页存储过程(效率最高)

create procedure proc_page_withtopmax ( @pageIndex int,--页索引 @pageSize int,--每页显示数 @pageCount int output,--总页数,输出参数 @totalCount int output--总条数 ) as begin set nocount on; declare @sql nvarchar(1000) set @sql='select top 10 * from tb_testtable where (

SQL server 变量if,while,存储过程

一.变量 1.if循环 2. 3.while循环 declare @ss int set @ss =2while @ss<10begin print 'Hello' set @[email protected]+1end --break 跳出循环 declare @sss int set @sss=2 while @sss<10begin print'Hello' set @[email protected]+1if @sss=6 breakend --continue 跳出本次循环,继续下次

SQL Server判断数据库、表、存储过程、函数是否存在

--判断数据库是否存在 if exists (select * from sys.databases where name = '数据库名') drop database [数据库名] --判断表是否存在 if exists (select * from sysobjects where id = object_id(N'[表名]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [表名] --判断存储过程是否存在 if exist