【问题标题】:Optional parameters in where clausewhere 子句中的可选参数
【发布时间】:2015-01-23 14:54:14
【问题描述】:

我有一个 C# 表单,用于在具有不同选项的表中进行搜索,用户可以过滤搜索过程。

我有 4 个文本框,例如 txt1 、 txt2 、txt3 和 txt4。

用户可以填写每个文本框进行搜索,例如 txt1 或 txt1 和 txt2 或 txt1 和 txt 和 txt4 或所有这些文本框,而将另一个留空并使用这些文本框的所有组合。那么如何编写一个选择过程来涵盖所有这些选项。

我写了这个程序,但它没有帮助:

CREATE proc sp_searchZ
@minprice bigint=null,@maxprice bigint=null,@minarea int=null,@maxarea int=null,@location nvarchar(50)=null,@kind nvarchar(50)=null
as

SELECT *
  FROM Landtbl
 WHERE ((@minprice is null and @maxpriceis null) or ([Price] between @minprice and @maxprice))
 and
       ((@minarea is null and @maxarea is null) or ([area] between @minarea and @maxarea))

 and   ((@location is null)or([location]=@location))
 and   ((@kind is null) or ([kind]=@kind))

【问题讨论】:

  • “它不会有帮助”是什么意思?我认为您的基本逻辑是合理的,您是否期望获得零记录?
  • @maxpriceis null 是您的存储过程中的错字还是仅在这个问题中?
  • 对不起,我知道了....但是忘记了我的代码...当用户动态过滤和填充一些文本框时我该怎么办?
  • 在继续之前,您需要阅读这篇关于捕获所有查询的文章。 sqlinthewild.co.za/index.php/2009/03/19/catch-all-queries
  • 另外两个 cmets。您不应该在实际代码中使用 select *。第二个是您的过程名称的前缀。我个人不能忍受前缀,因为它们不会增加清晰度,并且更难在列表中找到它们。我在这里说什么的原因是因为你的前缀是“sp_”。这是由 MS 为系统过程保留的。这意味着如果您不定义架构,引擎将首先在 sys 架构中查找,如果 sql server 有更新,它可能会导致您的 proc 无法使用。

标签: c# sql-server stored-procedures


【解决方案1】:

我认为实现这一点的最佳方式是使用动态 T-SQL。在您的存储过程中,您可以执行以下操作:

DECLARE @sqlString nvarchar(max);
DECLARE @sqlWhere nvarchar(max);
DECLARE @sqlParams nvarchar(max);

SET @sqlString = 'SELECT * FROM Landtbl '
SET @sqlWhere = 'WHERE 1=1 '

IF (@minprice IS NOT NULL and @maxprice IS NOT NULL)
BEGIN
    SET @sqlWhere = @sqlWhere + 'AND [Price] BETWEEN @minprice AND @maxprice '
END;

IF (@location IS NOT NULL)
BEGIN
    SET @sqlWhere = @sqlWhere + 'AND [location]=@location '
END;
-- any other conditional logic

SET @sqlParams = N'@minprice bigint,@maxprice bigint,@minarea int,@maxarea int,@location nvarchar(50),@kind nvarchar(50)';

SET @sqlString = @sqlString + @sqlWhere;

EXEC sp_executesql @sqlString, @sqlParams, @minprice ,@maxprice ,@minarea,@maxarea,@location,@kind

这应该很好用,可以用来做一些非常复杂的事情。我希望这会有所帮助

【讨论】:

  • tnx 但我不知道 EXEC sp_executesql 是什么意思。我必须在程序结束时写出来?这段代码到底是做什么的?
  • EXEC sp_executesql 将运行您动态创建的命令。它内置在 SQL 中,因此您无需执行任何操作。您仍然可以照常调用您的存储过程。你试过了吗?将我所有的代码放在一个 storedProc 定义中,声明适当的参数并运行它
  • 我可以从我的程序中删除这最后一行吗?如果可以。我如何编辑这个程序?
  • 我使用 linq 2 sql,当我想将此过程添加到我的程序时它说:未知返回类型和无法检测到以下存储过程的返回类型。然后问我自己设置它.那么我可以用这个消息做什么?
【解决方案2】:

这里不需要动态 SQL,我一直在做这种事情,就像这样(记得我说过你的逻辑是合理的吗?):

SELECT * FROM table WHERE ((@optionalparam IS NULL) OR ([field]=@optionalparam))

您已经这样做了,因此,如果您没有看到预期的结果,请注释掉所有 where 子句并一次添加每一位,您很快就会发现不是的位工作。

编辑:

试试这个,打开 SSMS 并打开一个新的查询窗口并输入以下内容:

DECLARE @minprice INT /* or whatever datatype you are using */
DELCARE @maxprice INT
SELECT @minprice=1, @maxprice=9999

SELECT 
  *
FROM 
  Landtbl
WHERE 
     ((@minprice IS NULL and @maxprice IS NULL) OR ([Price] BETWEEN @minprice AND @maxprice))

如果可行,则添加下一位

SELECT 
  *
FROM 
  Landtbl
WHERE 
     ((@minprice IS NULL and @maxprice IS NULL) OR ([Price] BETWEEN @minprice AND @maxprice))
 AND ((@minarea is null and @maxarea is null) or ([area] between @minarea and @maxarea))

如果有效,则添加下一位,依此类推,直到失败。您最后添加的位是无效的。

【讨论】:

  • 实际上,动态 sql 在这里是一种合理的方法,但从 sql 2008 开始就不需要了。这是几年前做到这一点并保持性能可接受的唯一方法。您使用的模式也是我的首选方法,但您需要确保添加 OPTION RECOMPILE,这样您就不会获得因参数不同而效率低下的陈旧执行计划。
  • @Sean,是的,完全同意你所说的一切,但我不想在这一点上过于复杂,因为 OP 在让实际查询工作时似乎有点麻烦。
  • 我听不懂。请您解释一下
  • @arshiadivsalar 好的,但你需要告诉我你的问题是什么
  • @DaveBecker 我的应用程序的另一个问题是,当我想将从文本框获得的内容转换为 int64(因为存储过程中存在 bigint 变量)时,我遇到了“无法从 System.DateTime 转换为'string' 错误....我认为这是因为一些空的文本框
猜你喜欢
  • 2012-04-28
  • 1970-01-01
  • 2013-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多