带有可选“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#中调用的存储过程正确插入数据而不提供所有可选参数?