【问题标题】:Stored Procedure with optional "WHERE" parameters带有可选“WHERE”参数的存储过程
【发布时间】:2010-10-16 09:48:50
【问题描述】:

我有一个表单,用户可以在其中指定各种参数来挖掘一些数据(状态、日期等)。

我可以生成如下查询:

SELECT * FROM table WHERE:
status_id = 3
date = <some date>
other_parameter = <value>

等等。每个WHERE 都是可选的(我可以选择所有带有status = 3 的行,或者所有带有date = 10/10/1980 的行,或者所有带有status = 3 AND date = 10/10/1980 的行等)。

给定大量参数,都是可选的,组成动态存储过程的最好方法是什么?

我正在研究各种数据库,例如: MySQL、Oracle 和 SQLServer。

【问题讨论】:

    标签: sql mysql sql-server oracle


    【解决方案1】:

    实现此目的的最简单方法之一:

    SELECT * FROM table 
    WHERE ((@status_id is null) or (status_id = @status_id))
    and ((@date is null) or ([date] = @date))
    and ((@other_parameter is null) or (other_parameter = @other_parameter))
    

    等等。 这完全消除了动态 sql 并允许您搜索一个或多个字段。通过消除动态 sql,您消除了有关 sql 注入的另一个安全问题。

    【讨论】:

    • 这不允许所有参数都是可选的。在这个例子中,@status_id 必须传入。即使它是 null,你也必须传入 null 才能执行。
    • 您可以为 MS SQL Server 中的参数提供默认值。我不了解 MySQL
    • 请记住,根据您的 RDBMS 及其缓存查询计划的方式,您可能无法使用此方法获得最佳性能。
    • 这样做的另一个危险是,您设法以某种方式将 NULL 传递给所有内容,然后每一行都返回。我在一个应用程序中看到了这种情况,它试图将 100 万多行发送回客户端。
    • 更多关于 SQL Server 中与此模式相关的潜在性能问题here 在存储过程中使用时 - 如果涉及的表具有大量行,参数嗅探可能会成为问题
    【解决方案2】:

    你可以这样做

    WHERE 
    (
     ParameterA == 4 OR ParameterA IS NULL
    )
    
    AND
    (
     ParameterB == 12 OR ParameterB IS NULL
    )
    

    【讨论】:

      【解决方案3】:

      像这样创建你的程序:

      CREATE PROCEDURE [dbo].[spXXX]
          @fromDate datetime = null,
          @toDate datetime = null,
          @subCode int = null
      as
      begin
      set NOCOUNT ON
      /* NOCOUNT limits the server feedback on select results record count */
      SELECT
          fields...
      FROM
          source
      WHERE
          1=1
      --Dynamic where clause for various parameters which may or may not be passed in.
      and ( @fromDate is null or [dateField] >= @fromDate)
      and ( @toDate is null or [dateField] <= @toDate)
      and ( @subCode is null or subCode= @leaveTypeSubCode)
      order by fields...
      

      这将允许您使用 0 个参数、所有参数或任何 # 个参数来执行过程。

      【讨论】:

        【解决方案4】:

        如果您想避免动态构建 SQL 字符串(这通常是最好避免的),您可以在存储过程中通过将 where 子句中的每个标准与默认值进行比较来执行此操作,这相当于“忽略”。例如:

        select * from Table where
           (@Col1 IS NULL OR Col1 = @Col1) /*If you don't want to filter in @col, pass in NULL*/
           AND
           (@Col2 IS NULL OR Col2 = @Col2)
        

        【讨论】:

          【解决方案5】:

          这是我使用的样式:

          t-sql

          SELECT    *        
          FROM    table        
          WHERE     
          status_id    =    isnull(@status_id ,status_id)     
          and    date    =    isnull(@date ,date )     
          and    other_parameter    =    isnull(@other_parameter,other_parameter) 
          

          甲骨文

          SELECT    *        
          FROM    table        
          WHERE     
          status_id    =    nval(p_status_id ,status_id)     
          and    date    =    nval(p_date ,date )     
          and    other_parameter    =    nval(p_other_parameter,other_parameter)
          

          【讨论】:

          【解决方案6】:

          一种可读且可维护的方式(甚至可用于 JOIN/APPLY):

          where 
                (@parameter1 IS NULL OR your_condition1)
            and (@parameter2 IS NULL OR your_condition2) 
          -- etc
          

          但是,在大多数大表(甚至更多使用 JOIN/APPLY)上这是一个坏主意,因为您的执行计划不会忽略 NULL 值并产生大量性能漏洞(例如:扫描所有表以搜索 NULL 值)。

          SQL Server 中的一种迂回方式是在查询中使用 WITH(RECOMPILE) 选项(自 SQL 2008 SP1 CU5 (10.0.2746) 起可用)。

          实现这一点的最佳方法(性能方面)是使用 IF ... ELSE 块,每个组合一个。也许这很累,但你会得到最好的表现,而且你的数据库设置并不重要。

          如果您需要更多详细信息,可以查找 KM。回复here

          【讨论】:

            猜你喜欢
            • 2018-11-08
            • 2010-12-15
            • 1970-01-01
            • 1970-01-01
            • 2023-03-26
            • 1970-01-01
            • 1970-01-01
            • 2018-07-16
            • 1970-01-01
            相关资源
            最近更新 更多