【问题标题】:SQL Server CASE in WHERE with single condition but multiple returnsSQL Server CASE in WHERE 具有单个条件但多个返回
【发布时间】:2021-04-01 18:40:11
【问题描述】:

用户故事

我需要选择列来检查空值,并且我的选择中的关系是 OR(例如 Cost IS NULL 或 PurchaseType IS NULL,或两者都是 NULL)。

发展问题

我的应用程序有几个以用户进行选择为条件的查询。例如,如果选择字段被命名为 @selection1 和 @selection2,那么我的 where 子句如下所示:

select...
from...
where isActive=1 
and (@selection1=column or @selection1='')
and (@selection2=column or @selection2='')

这个特殊的用例要求我建立 OR 关系而不是 AND,这在我的逻辑中添加了一个皱纹:

select..
from...
where isActive=1
and ((@selection1=column or @selection1='')
or (@selection2=column or @selection2=''))

如果任何一个选择为空(每行后半部分为''=''),则整个子句返回 true 并被跳过,返回所有行并忽略用户的有效选择。

代码示例

DECLARE @filter AS VARCHAR(100)=''
/**Options are empty
, Cost to show only where Cost IS NULL
, PurchaseType to show only where PurchaseType IS NULL
, PurchaseQuantity to show only where PurchaseQuantity IS NULL
, or any combination of these 3 values to show where ANY COLUMN in the combination IS NULL
so if the user selects 'Cost,PurchaseQuantity', then they want columns 1,2,3,4,7
and if the user selects 'PurchaseType,PurchaseQuantity', then they want columns 1,2,3,4,6.
**/

-- Create Sample Table
IF OBJECT_ID('tempdb..#Product') IS NOT NULL
    Truncate TABLE #Product
ELSE
CREATE TABLE #Product (id INT, Cost DEC(8,2), PurchaseType VARCHAR(20), PurchaseQuantity INT, isActive bit);
INSERT INTO #Product 
 VALUES 
(1, NULL, NULL, NULL,1),
(2, 10.00, NULL, NULL,0),
(3, NULL, 'Each',NULL,1),
(4, NULL, NULL, 100,1),
(5, 5.50, 'Box',1,0),
(6, 142.00, NULL, 12,1),
(7, NULL, 'Pkg',50,1)

-- Select values from Sample Table
-- Works great for single @filter values, but fails for multiple
--SELECT id,Cost,PurchaseType,PurchaseQuantity FROM #Product
--WHERE
--  CASE WHEN @filter = 'Cost' THEN Cost
--      ELSE 
--      CASE WHEN @filter='PurchaseType' THEN PurchaseType
--      ELSE    
--      CASE WHEN @filter='PurchaseQuantity' THEN PurchaseQuantity
--      ELSE    
--      CASE WHEN @filter='Cost,PurchaseType' THEN COALESCE(Cost,PurchaseType)
--      --CASE WHEN @filter='Cost,PurchaseType' THEN Cost or PurchaseType
--      --The above line expresses what I'm trying to do in pseudo, but doesn't work for resulting when Cost IS NULL or PurchaseType IS NULL
--      ELSE    
--      CASE WHEN @filter='Cost,PurchaseQuantity' THEN COALESCE(Cost,PurchaseQuantity)
--      ELSE    
--      CASE WHEN @filter='PurchaseQuantity,PurchaseType' THEN COALESCE(PurchaseQuantity,PurchaseType)
--      ELSE    
--      CASE WHEN @filter='Cost,PurchaseQuantity,PurchaseType' THEN COALESCE(Cost,PurchaseQuantity,PurchaseType)
--      END END END END END END END
--      IS NULL

select id,Cost,PurchaseType,PurchaseQuantity,isActive
    from #Product
    where isActive=1 
    and (@filter !='' and
    (
     (Cost is null and @filter like '%Cost%')
     or
     (PurchaseType is null and @filter like '%PurchaseType%')
     or
     (PurchaseQuantity is null and @filter like '%PurchaseQuantity%')
    ))

我添加了@Stu 的逻辑,但是我的生产架构中的“isActive=1 and”和其他逻辑意味着如果用户将过滤器留空而不是忽略@Filter 部分(这就是我想看到发生)。

如我的代码示例中所述,我最终希望设置 where 子句,以便

  • 如果用户没有为 @filter 选择任何内容,则跳过 WHERE
  • 如果用户为 @filter 选择单个值,则 WHERE 返回该单个列为 NULL 的位置
  • 如果用户为 @filter 选择多个值,则 WHERE 将返回任何 @filter 列为 NULL 的位置

我已经花了一天半的时间来解决这个问题......希望我错过了一些简单的事情,并且互联网上的某个人有办法解决这个问题。提前致谢!!

【问题讨论】:

  • 恐怕这只是一个糟糕的设计。不要走这条路。为您可能要过滤的每个列传递参数,并设计一个“catch&all”/“kitchen sink”查询。
  • 这是一个设计的母马,如果你有 5 列或 10 列呢?!
  • 我的 2 美分 - 不要给用户太多的灵活性。随着时间的推移,提供这种服务的成本很高,而且用户实际上很少使用它。我发现用户通常只需要几个搜索选项,并且通常使用更小的集合。

标签: sql-server case where-clause sql-null


【解决方案1】:

我无法理解您是如何提出这种安排的,但是这对您有用吗?很难确定!

    select id,Cost,PurchaseType,PurchaseQuantity,isActive 
    from #Product
    where isActive=1 and
    (@Filter ='' or
    (
     (Cost is null and @filter like '%cost%')
     or
     (PurchaseType is null and @filter like '%PurchaseType%')
     or
     (PurchaseQuantity is null and @filter like '%PurchaseQuantity%')
    ))

请注意,这几乎肯定会导致查询性能不佳,尤其是当表很大时 - 有 1000 多行。

最好执行 3 个单独的查询并 union 结果 - 这样优化器可以在每个查询上使用合适的索引。

【讨论】:

  • 我更新了我的示例以包含@Filter 可以为空的要求,并且我需要 WHERE 子句的其他部分在没有它的情况下运行。也许你可以再试一次??
猜你喜欢
  • 2020-06-16
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 2019-05-22
  • 1970-01-01
  • 2020-12-13
  • 2015-09-23
  • 1970-01-01
相关资源
最近更新 更多