【问题标题】:How to ignore a parameter in stored procedure if its value is null如果其值为空,如何忽略存储过程中的参数
【发布时间】:2015-08-23 12:30:13
【问题描述】:

我有一个存储过程,我必须在其中加入 10 个表并使用WHERE 条件根据存储过程中传递的参数过滤记录。例如:

create procedure proc1
    @var1 varchar(100) = null,
    @var2 varchar(100) = null,
    @var3 varchar(100) = null,
    @var4 varchar(100) = null,
    ........   
    @var10 varchar(100) = null
as
begin
    insert into #a
    select * from
    (
        select * from 
            tab1 as a
            inner join tab2 as b on a.rollnumber = b.rollnumber
            inner join tab3 as c on c.city = b.city
            ........
            inner join tab10 as j on J.id = i.id
        where 
            a.id = isnull(@var1,a.id) and 
            b.id = isnull(@var2,b.id) and 
            c.id = isnull(@var3,c.id) and 
            ...........
            J.id = isnull(@var10,j.id)
    ) as abc

    if (select count(*) from #a) < 10 
    begin
        select * from #a
    end
    else 
    begin
        print 'Cannot display the records as count is more than 10'
    end
end

上面的存储过程运行良好,但是速度很慢,因为WHERE 子句中有10 个条件。如果某些参数未提供给存储过程,我想要的是跳过条件。例如,如果只有 3 个参数传递给存储过程,则 WHERE 子句应跳过 WHERE 子句中的其余参数。这将使程序更有效率。所以,如果@var1没有被传递,那么a.id的所有值都应该被返回。

【问题讨论】:

    标签: sql sql-server sql-server-2008 tsql


    【解决方案1】:

    Erland Sommarskog 有一篇很好的文章Dynamic Search Conditions in T‑SQL。他解释了几种可以使用的方法,并比较了 @lad2025 建议的构建动态 SQL 和使用 OPTION(RECOMPILE) 的方法。

    我个人在这些查询中使用OPTION(RECOMPILE)。您使用 SQL Server 2008,因此此选项是一个不错的选择。如果您确实通过动态 SQL 路由,请务必阅读他的另一篇文章 The Curse and Blessings of Dynamic SQL

    所以,你的程序变成了这样:

    create procedure proc1
        @var1 varchar(100) = null,
        @var2 varchar(100) = null,
        @var3 varchar(100) = null,
        @var4 varchar(100) = null,
        ........   
        @var10 varchar(100) = null
    as
    begin
        insert into #a
        select * from
        (
            select * 
            from
                tab1 as a
                inner join tab2 as b on a.rollnumber = b.rollnumber
                inner join tab3 as c on c.city = b.city
                ........
                inner join tab10 as j on J.id = i.id
            where 
                (a.id = @var1 OR @var1 IS NULL)
                and (b.id = @var2 OR @var2 IS NULL)
                and (c.id = @var3 OR @var3 IS NULL)
                ...........
                and (J.id = @var10 OR @var10 IS NULL)
        ) as abc
        OPTION(RECOMPILE);
    
        if (select count(*) from #a) < 10 
        begin
            select * from #a
        end
        else 
        begin
            print 'Cannot display the records as count is more than 10'
        end
    end
    

    顺便说一句,通过检查count() 并不清楚您想要实现什么,但也许您只需要简单的TOP(10) 最多返回10 行。如果您确实使用TOP 来一致地返回结果,请确保添加ORDER BY 子句。如果您不知道,您可以在过程中使用另一个参数来指示要返回的最大行数,并在TOP(@ParamMaxRowCount) 中使用它。存储过程有时会返回结果集,有时只打印一条消息,这种情况并不常见。

    【讨论】:

      【解决方案2】:

      让我们来玩动态 - SQL:

      CREATE PROCEDURE [dbo].[my_procedure]
           @var1 VARCHAR(100) = NULL,
           @var2 VARCHAR(100) = NULL,
           @var3 VARCHAR(100) = NULL,
           @var4 VARCHAR(100) = NULL,
           /* ........  */ 
           @var10 VARCHAR(100) = NULL,
           @debug INT         = 0
      AS
      BEGIN
      SET NOCOUNT ON;
      
      DECLARE 
          @sql        NVARCHAR(MAX),                                
          @paramlist  NVARCHAR(4000),                              
          @nl         CHAR(2) = CHAR(13) + CHAR(10);
      
      
      /* Main query here */
      SELECT @sql = 
         'SELECT * 
          FROM tab1 AS a
          INNER JOIN tab2 AS b
            ON a.rollnumber = b.rollnumber
          INNER JOIN tab3 AS c
            ON c.city = b.city
          /* ........ and so on */
          INNER JOIN tab10 AS j
            ON J.id = i.id
          WHERE 1 = 1 ' + @nl;
      
      IF @var1 IS NOT NULL                                            
          SELECT @sql += ' AND a.id = @var1' + @nl;  
      
      IF @var2 IS NOT NULL                                            
          SELECT @sql += ' AND b.id = @var2' + @nl;  
      
      /* ... */
      
      IF @var10 IS NOT NULL                                            
          SELECT @sql += ' AND j.id = @var10' + @nl;
      
      /* If sorting needed just add it;
      SELECT @sql += ORDER BY a.id;
      */
      
      IF @debug = 1
          PRINT @sql;
      
      
      SELECT @paramlist = 
            '@var1     VARCHAR(100),   
             @var2     VARCHAR(100),
             /* ... */
             @var10     VARCHAR(100)';
      
      /*CREATE TABLE #temp definition here */
      
      INSERT INTO #temp(col1, col2, ...)
      EXEC [dbo].[sp_executesql]
                 @sql,
                 @paramlist,
                 @var1,
                 @var2,
                 /* ... */
                 @var10;
      
      IF @@ROWCOUNT < 10 
      THEN
          SELECT *       /* Use column names */
          FROM #temp;
      ELSE
          PRINT 'Cannot display the records as count is more than 10';
      END
      

      如果想查看查询,可用作任何普通存储过程 + 调试:

       EXEC [dbo].[my_procedure]
                  @var1 = 'AAA'
                 ,@var2 = 'BBB'
                 ,@debug = 1;
      
      • 格式决定一切,尤其是在使用 Dynamic-SQL 时。
      • @@ROWCOUNT代替SELECT COUNT(*) FROM #temp
      • 从 SP 打印不是好的做法,最好返回一些值以在 OUT 参数中指示它。
      • 如果您不喜欢 WHERE 1 = 1,您可以先检查所有参数是否为 NULL,然后跳过它。
      • 您根本不需要使用动态 SQL,只需使用 IF THEN ELSE IF THEN ELSE IF ... ELSE 并大量复制难以维护的代码(如果代码行是用于衡量您的生产力的指标,它可能很有用)

      【讨论】:

        【解决方案3】:

        你只需要给出正在使用的参数。

        如果只需要传递 Var1 的值。

        proc1 @Var1 = Var1Value
        

        如果需要传递 Var6 的值。

        Proc1 @Var6 = Var6Value
        

        请像这样用例

         where case @var1 when null then 1 else  a.id end =  case @var1 when null then 1 else  @var1 end 
        

        希望对你有所帮助 谢谢

        【讨论】:

        • 这无济于事。 where 子句中的所有条件仍将被使用
        猜你喜欢
        • 2012-03-02
        • 2011-11-22
        • 2020-06-05
        • 1970-01-01
        • 2018-09-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-31
        相关资源
        最近更新 更多