SQL Server 2008 - 条件查询

4

SQL不是我的强项。我有一个SQL Server 2008数据库。这个数据库有一个存储过程,需要输入八个int参数。出于让问题更加明确的考虑,我将使用其中一个参数作为参考:

@isActive int

每个int参数都会是-1、0或1。-1表示“未知”或“不关心”。基本上,我需要查询一个表格,以便如果int参数不是-1,则需要在我的WHERE子句中考虑它。因为有八个int参数,if-else语句似乎不是一个好主意。同时,我不知道还有其他方法可以做到这一点?
在SQL中是否有一种优雅的方式,在参数不等于某个值时添加WHERE条件?
谢谢!
6个回答

5

最佳动态搜索条件的来源:

Erland Sommarskog的T-SQL中的动态搜索条件

如何执行此操作以确定是否可以使用索引存在许多微妙的影响。如果您在SQL Server 2008的适当版本上,只需将OPTION (RECOMPILE)添加到查询中,运行时使用本地变量的值进行优化。

请考虑,OPTION (RECOMPILE)将处理这段代码(其中不能使用任何索引):

WHERE
    (@search1 IS NULL or Column1=@Search1)
    AND (@search2 IS NULL or Column2=@Search2)
    AND (@search3 IS NULL or Column3=@Search3)

并在运行时进行优化,以使其(只要传递了值@Search2):

WHERE
    Column2=@Search2

如果你在Column2上定义了索引,那么就可以使用索引。


@Marcus Adams,是的,您说得对。我习惯于在“忽略”搜索条件时传递NULL,因此我只是这样编写了示例。因此,根据OPs问题,将它们编码为 (@Search1=-1 OR Column1=@Search1) AND ... - KM.
KM,似乎除了我发布的内容之外,这里其他那些不理解自己所写内容含义的白痴中,你是唯一一个聪明的人。恭喜——特别是针对(recompile)选项,我懒得去查(我从来不用存储过程进行查询)。 - TomTom

1
WHERE coalesce(active,1) = (CASE 
                              WHEN @isActive = -1 THEN coalesce(active,1)
                              ELSE @isActive
                            END)

是的,可悲的是,不止有8种情况 - 事实上有8!种情况,这将是一个庞大的查询。 - TomTom
@TomTom,每个参数只能有一个CASE语句。我不认为这是个问题。 - Marcus Adams
1
@TomTom,这是一个部分答案。完整的解决方案将使用8个CASE语句,而不是一个带有8!表达式的CASE语句。 - Marcus Adams
1
尝试执行以下代码:if null = null print 'wow',你会发现 "wow" 从未被打印出来。如果你将 @isActive 参数设置为 -1,那么在 active 列为空时,你将得不到任何行。因此,如果你的列可以为空,这种方法就不起作用了。 - KM.
@KM - 很好的观点,我编辑了我的答案以考虑到这一点。 - dcp
显示剩余7条评论

1

模式 (column = @param OR @param IS NULL) 将为您提供可选参数。 您可以使用 NULLIF 使您的-1无效。 更好的方法是允许空参数,而不是使用 魔术数字

WHERE
   (Customer.IsActive = NULLIF(@isActive, -1) OR NULLIF(@isActive, -1) IS NULL)

你有4个"(",但只有3个")",如果你改成(Customer.IsActive = NULLIF(@isActive, -1) OR (NULLIF(@isActive, -1) IS NULL))就可以了。 - KM.

1

与其使用-1表示您不知道或不关心,为什么不直接使用Null呢?这基本上是它的用途。然后,您可以切换到位而不是整数。

此外,我相信TomTom会持不同意见,但我认为对于这些东西,使用CASE语句是正确的方法。

您的结果可能会有所不同,但似乎查询引擎处理它要好得多,而不是将事物包装在IsNull中或具有多个OR语句,随着您开始添加其他条件,情况可能会变得相当混乱。

无论您选择哪种方式,执行计划都会因传入的内容而略微受到影响,但不应该太可怕。

使用CASE语句的额外好处是,您可以添加一些复杂性,而不需要太多额外的代码(与使用许多OR语句相比)。此外,满足您标准的第一个条件可以防止额外的评估,而在处理OR时并非总是如此...

因此,对于8个可选参数,值为-1用于忽略搜索,您最终得到的是:

WHERE
        @Search1 = CASE WHEN @Search1 = -1 THEN @Search1 ELSE @Column1 END
    AND @Search2 = CASE WHEN @Search2 = -1 THEN @Search1 ELSE @Column2 END
    AND @Search3 = CASE WHEN @Search3 = -1 THEN @Search1 ELSE @Column3 END
    AND @Search4 = CASE WHEN @Search4 = -1 THEN @Search1 ELSE @Column4 END
    AND @Search5 = CASE WHEN @Search5 = -1 THEN @Search1 ELSE @Column5 END
    AND @Search6 = CASE WHEN @Search6 = -1 THEN @Search1 ELSE @Column6 END
    AND @Search7 = CASE WHEN @Search7 = -1 THEN @Search1 ELSE @Column7 END
    AND @Search8 = CASE WHEN @Search8 = -1 THEN @Search1 ELSE @Column8 END

注意:正如KM所指出的,如果你处理的列可能具有NULL值,那么使用NULL方法就会不够用,因为NULL=NULL无法正确评估。所以,出于好玩,我将我的答案改回了原始帖子请求的内容,即使用他们自己的标识符来跳过搜索。


天啊,你曾经在真实的数据库上尝试过这个吗?不是太可怕吗?完全取决于存储的查询计划 - 我曾经遇到过由于第一个查询以表扫描结束而导致的事情变成了3分钟的怪物的情况。 - TomTom
使用OR代替CASE解决了吗?如果没有适当的索引并且有大量数据,您可能会遇到问题。我在拥有数百万条记录的表上运行过此类查询。肯定不需要几分钟。但是,就像我说的,您的情况可能会有所不同。这在很大程度上取决于表设计、正在查询什么、有多少数据以及是什么类型的数据。有许多方法来解决这个问题。只是不明白为什么您对除自己之外的每个解决方案都如此批判。 - Kevin Fairchild
1
尝试执行以下代码:if null = null print 'wow',你会发现 "wow" 从未被打印出来。如果你将 @search1 参数设置为 NULL,那么在 Column1 列中包含 NULL 的行将不会返回任何结果。因此,如果你的列可以为空,则此方法无法正常工作。 - KM.

0

.... Where field=case @isActive WHEN -1 THEN field ELSE @isActive END ....

.... 当 field=case @isActive WHEN -1 THEN field ELSE @isActive END 时 ....


做一些基本的数学计算,看看你需要多少组合 - 你会感到惊讶。这将是一个无所获得的庞大存储过程。 - TomTom
你的意思是什么?这对于具有许多参数的非常大的查询非常有效。 - Alex
真的吗?8个可选参数 - 你会在哪里/何时得到这么多? ;) 小提示:它并没有说只有一个参数是可选的 - 那么所有2个可选参数的组合,3个可选参数的组合,4个可选参数的组合呢? - TomTom
尝试执行此语句:if null = null print 'wow',您会发现 "wow" 从未被打印出来。如果将 @isActive 参数设置为 -1,则不会返回任何字段列为空的行。因此,如果您的列可以为 null,则无法使用此方法。 - KM.
汤姆汤姆,有什么问题吗?您为每个参数编写了一个case语句。 查询文本的线性复杂度。KM,此案例未由询问者指定。在这种情况下,isnull()是适当的。Coalesce也很好,但它与isnull()相同,只是对于更多的变量,因为它为coalesce(@ var,@ var2,@ var3)生成isnull(@ var,isnull(@ var2,@ var3))。 - Alex

-5

没有一种优雅的方式 - 所有的方式都很糟糕。

WHERE @isActive == -1 OR isActive = @isActive

基本上只有这种方式 - 但即便如此,请确保每次重新评估查询计划,否则大多数查询将使用错误的查询计划。

这是一个典型的存储过程不好的案例。我认为,自从现代时代开始(大约15年前),就不应该再使用存储过程进行查询 - 这是因为有人足够聪明,写出了第一个ORM。


2
存储过程非常适合限制对实际表的访问,并通过存储过程提供可直接使用的数据。 - Alex
真的吗?这怎么比允许用户动态定义查询参数的视图更好呢?阅读http://weblogs.asp.net/fbouma/archive/2003/11/18/38178.aspx,了解您的逻辑有多么糟糕。 - TomTom
3
TSQL不使用"==",请参见Comparison Operators (Transact-SQL),我认为你想要表达的是 WHERE @isActive = -1 OR isActive = @isActive - KM.
视图的功能远不如存储过程。例如,你可以使存储过程返回一个值,从而可以确定是否存在错误。此外,存储过程允许使用复杂算法来查看数据以及修改数据。不要再做一个傲慢的开发者了,看看其他实践方法,你可能会学到更多东西,而不是坚持你对存储过程的恐惧。 - Alex
停止让自己变成白痴。视图允许我事后定义过滤条件,并且还可以像存储过程一样执行所有相同的操作(甚至可能使用一些技巧)。查询的灵活性至关重要。20年前我使用过存储过程 - 如今在90%的应用程序中,我珍视不必维护它们所节省的时间=节省的金钱。 - TomTom
4
你自讨没趣。像你这样的喷子,我不得不再次查找手册,再次阅读它,并发现视图是用于查看数据的,因为视图只是视图,不应该做任何能让你查看的事情。 我感到震惊!在业界工作20年,还什么都没学到?!!做你一定很糟糕。 http://msdn.microsoft.com/en-us/library/aa258253(SQL.80).aspx - Alex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接