SQL Server-聚焦WHERE Column = @Param OR @Param IS NULL有问题?

前言

上一篇我们讲完SQL动态查询,本节我们继续来讲解SQL动态查询中存在的问题。

SQL动态查询条件筛选过滤

当我们创建存储过程调用存储过程时,若筛选条件有值则过滤,没有值则返回所行记录,类似如下查询:

WHERE (SomeColumn=@col OR @col IS NULL)

这样查询会存在什么问题呢?性能会不会有问题呢,这个是我们本节需要深入探讨的问题。

接下来我们创建如下测试表并插入测试数据,如下:

CREATE TABLE Test
    (
      SomeCol1 INT NOT NULL ,
      Somecol2 INT NOT NULL
    )
 
INSERT  Test
        SELECT  number ,
                low
        FROM    master..spt_values
        WHERE   TYPE = 'p'
 
 
CREATE INDEX ix_col2 ON Test(Somecol2)
GO

对于动态SQL条件筛选过滤我们利用WHERE 1 = 1来拼接。接下来我们使用一般SQL语句和动态查询并比较其IO,如下:

SET STATISTICS IO ON
GO
 
DECLARE @col INT
SELECT @col = 1
 
SELECT SomeCol2 
FROM Test
WHERE 1 =1
AND  (SomeCol2=@col OR @col IS NULL)
 
GO
 
DECLARE @col INT
SELECT @col = 1
 
DECLARE @SQL NVARCHAR(4000)
    SET @SQL = 'SELECT SomeCol2 
                FROM Test
                WHERE 1 =1'
 
IF @col IS NOT NULL 
    SET @SQL = @SQL + ' AND SomeCol2=@InnerParamcol '
    
EXEC sp_executesql @SQL,N'@InnerParamcol INT',@col
 
SET STATISTICS IO OFF
GO


我们能够看到动态SQL查询逻辑读取只读取2次,而另外一般SQL语句查询逻辑读取7次,同时我们看到SQL动态查询计划执行的是索引查找,而一般SQL语句则是索引扫描。

看来执行一般SQL语句不会走索引查找,将导致性能问题,在开头我们就讲过筛选条件有值则过滤,无值则查询所有数据,那么我们完全可以借助ISNULL来查询,下面我们用ISNULL来改变一般语句筛选条件,看看是否会走索引查找呢?


SET STATISTICS IO ON
GO
 
DECLARE @col INT
SELECT @col = 1
 
SELECT SomeCol2
FROM dbo.Test
WHERE 1 = 1
AND  SomeCol2 = ISNULL(@col,SomeCol2)


我们看到结果依然是走索引扫描,没有任何改变。是不是就没有解决之道了呢?我们来改变一般SQL语句查询方式,如下:

DECLARE @col INT
SELECT @col = 1

IF @Col IS NULL
    SELECT  SomeCol2
    FROM    Test
    WHERE   1 = 1
ELSE
    SELECT  SomeCol2
    FROM    dbo.Test
    WHERE   1 = 1
            AND SomeCol2 = @col

GO

如上只能是勉勉强强解决了问题,因为只是针对一个参数,如果有多个参数要进行IF...ELSE..,那可就傻逼了。从本质上解决这个问题我们需要利用可选项重新编译。如下:


SET STATISTICS IO ON
GO
 
DECLARE @col INT
SELECT @col = 1
 
SELECT SomeCol2 
FROM dbo.Test
WHERE 1 =1
AND  (SomeCol2 = @col OR @col IS NULL)
OPTION(RECOMPILE)
 
 
GO
 
DECLARE @col INT
SELECT @col = 1
 
DECLARE @SQL NVARCHAR(4000)
    SET @SQL = 'SELECT SomeCol2 
                FROM dbo.Test
                WHERE 1 =1'
 
IF @col IS NOT NULL 
    SET @SQL = @SQL + ' AND SomeCol2 = @InnerParamcol '
    
    
    
EXEC sp_executesql @SQL,N'@InnerParamcol INT',@col
 
SET STATISTICS IO OFF
GO

总结

当利用条件筛选过滤数据时,如果条件有值则过滤,否则返回所有行记录。如果执行一般SQL语句和动态SQL,那么动态SQL会走索引查找,而一般SQL语句将导致索引扫描,此时需要加上OPTION(RECOMPILE)才走索引查找。


为了方便大家在移动端也能看到我分享的博文,现已注册个人公众号,扫描上方左边二维码即可,欢迎大家关注,有时间会及时分享相关技术博文。

感谢花时间阅读此篇文章,如果您觉得这篇文章你学到了东西也是为了犒劳下博主的码字不易不妨打赏一下吧,让楼主能喝上一杯咖啡,在此谢过了!
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!
本文版权归作者和博客园共有,来源网址:http://www.cnblogs.com/CreateMyself)/欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
posted @   Jeffcky  阅读(2170)  评论(12编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示