带有可选“WHERE”参数的存储过程

Posted

技术标签:

【中文标题】带有可选“WHERE”参数的存储过程【英文标题】:Stored Procedure with optional "WHERE" parameters 【发布时间】: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。

【问题讨论】:

【参考方案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)

【讨论】:

不要在 T-SQL 中使用。如果字段可以为空,则条件 NULL = NULL 不会返回 TRUE。见:sommarskog.se/dyn-search-2008.html(寻找聚结陷阱)【参考方案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

【讨论】:

以上是关于带有可选“WHERE”参数的存储过程的主要内容,如果未能解决你的问题,请参考以下文章

如何在C#中调用的存储过程正确插入数据而不提供所有可选参数?

存储过程中的 Oracle 条件 WHERE

where 条件中的可选参数 - 如何提高性能 - PL/SQL

如何在存储过程中设置日期时间可选参数?

SQL Server 存储过程中的可选参数?

SQL - 创建具有多个可选参数的存储过程