以一个简单的查询存储过程为例,简单说一下sql的几种写法( 二 )

一方面需要处理转移符 , 另一方面需要要防止SQL注入
其二 , 参数不同就必须重新编译
这种拼凑SQL的方式 , 如果每次查询的参数不同 , 拼凑出来的SQL字符串也不一样 , 
如果熟悉SQL Server的同学一定知道 , 只要你执行的SQL文本不一样 , 
比如
第一次是执行查询 *** where CustomerId = 'C88'  , 
第二次是执行查询 *** where CustomerId = 'C99'  , 因为两次执行的SQL文本不同
每次执行之前必然需要对其进行编译 , 编译的话就需要CPU , 内存资源
如果存在大批量的SQL编译 , 无疑要消耗更多的CPU资源(当然也需要一些内存资源)
第二种常见的写法:对所有查询条件用OR的方式加在where条件中 , 非常不推荐
create proc pr_getOrederInfo_2(@p_OrderNumberint,@p_CustomerIdvarchar(20) ,@p_OrderDateBegindatetime,@p_OrderDateEnddatetime)asbeginset nocount on;declare @strSql nvarchar(max);SELECT [id],[OrderNumber],[CustomerId],[OrderDate],[Remark]FROM [dbo].[SaleOrder]where 1=1and (@p_OrderNumber is nullor OrderNumber= @p_OrderNumber)and (@p_CustomerIdis nullor CustomerId= @p_CustomerId)/*这是另外一种类似的奇葩的写法,下面会重点关注andOrderNumber= ISNULL( @p_OrderNumber,OrderNumber)andCustomerId= ISNULL( @p_CustomerId,CustomerId)*/and (@p_OrderDateBegin is null or OrderDate>= @p_OrderDateBegin)and (@p_OrderDateEnd is nullor OrderDate<= @p_OrderDateEnd)end首先看这种方式的执行结果 , 带入同样的参数 , 跟上面的结果一样 , 查询(结果)本身是没有任何问题的

以一个简单的查询存储过程为例,简单说一下sql的几种写法

文章插图
 
这种写法写起来避免了拼凑字符串的处理 , 看起来很简洁 , 写起来也很快 , 稀里哗啦一个存储过程就写好了 , 
发布到生产环境之后就相当于埋了一颗雷 , 随时引爆 。
因为一条低效而又频繁执行的SQL , 拖垮一台服务器也是司空见惯
但是呢 , 问题非常多 , 也非常非常不推荐 , 甚至比第一种方式更糟糕 。
分析一下这种处理方式的逻辑:
这种处理方式 , 因为不确定查询的时候到底有没有传入参数 , 也就是说不能确定某一个查询条件是否生效 , 
于是就采用类似 and (@p_OrderNumber is null or OrderNumber = @p_OrderNumber)这种方式 , 来处理参数 , 
这样的话
如果@p_OrderNumber为null , or的前者(@p_OrderNumber is null)成立 , 后者不成立 , 查询条件不生效
如果@p_OrderNumber为非null , or的后者(OrderNumber = @p_OrderNumber)成立而前者不成立 , 查询条件生效
总之来说 , 不管参数是否为空 , 都可以有效地拼凑到查询条件中去 。
避免了拼SQL字符串 , 既做到让参数非空的时候生效 , 有做到参数为空的时候不生效 , 看起来不错 , 是真的吗?
那么这种存储过程的有什么问题?
1 , 会抑制索引的情况
如图 , 带入参数值执行存储过程 , 先忽略另外三个查询字段 , 只传入@p_CustomerId参数 , 
相关查询列上(CustomerId)有索引 , 但是这里走的是CustomerId列上的Index Scan而非预期的Index Seek
以一个简单的查询存储过程为例,简单说一下sql的几种写法

文章插图
 
纠错:上面的一句话 , 使用参数做编译的时候 , 是知道参数的值的(只有使用本地变量的时候才不知道具体的参数值 , 直接使用参数确实是知道的) , 
编译也是根据具体的参数值来生成执行计划的 , 但是为什么即使知道具体的参数值的情况下 , 依然生成一个Index Scan的方式 , 而不是期望的Index Seek?
即便是存储过程在编译的时候知道了参数的值 , 为什么仍旧用不到索引?
还要从and (@p_CustomerId is null or CustomerId = @p_CustomerId)这种写法入手分析 。
即便是CustomerId列上有索引 , 
如果@p_CustomerId 参数非空 , 走索引Seek完全没有问题 。
如果@p_CustomerId 为null , 此时and (@p_CustomerId is null or CustomerId = @p_CustomerId)这个条件恒成立 , 如果再走索引Seek会出现什么结果?


推荐阅读