SQL Server 中的条件 WHERE 子句

Posted

技术标签:

【中文标题】SQL Server 中的条件 WHERE 子句【英文标题】:Conditional WHERE clause in SQL Server 【发布时间】:2013-09-08 20:43:01 【问题描述】:

我正在创建一个 SQL 查询,其中我需要一个条件 where 子句。

应该是这样的:

SELECT 
    DateAppr,
    TimeAppr,
    TAT,
    LaserLTR,
    Permit,
    LtrPrinter,
    JobName,
    JobNumber,
    JobDesc,
    ActQty,
    (ActQty-LtrPrinted) AS L,
    (ActQty-QtyInserted) AS M,
    ((ActQty-LtrPrinted)-(ActQty-QtyInserted)) AS N
FROM 
    [test].[dbo].[MM]
WHERE 
    DateDropped = 0
            --This is where i need the conditional clause 
    AND CASE
            WHEN @JobsOnHold = 1 THEN DateAppr >=  0
            ELSE  DateAppr != 0
        END

上述查询无效。这不是正确的语法还是我不知道的另一种方法?

我不想使用动态 SQL,所以有没有其他方法或者我必须使用解决方法,例如使用 if else 并使用具有不同 where 子句的相同查询?

【问题讨论】:

在其他人回答后更改问题或代码(从DateAppr = 0DateAppr >= 0)...为什么? 对不起!我一开始就忘记了,所以只是纠正它 【参考方案1】:

试试这个

SELECT 
    DateAppr,
    TimeAppr,
    TAT,
    LaserLTR,
    Permit,
    LtrPrinter,
    JobName,
    JobNumber,
    JobDesc,
    ActQty,
    (ActQty-LtrPrinted) AS L,
    (ActQty-QtyInserted) AS M,
    ((ActQty-LtrPrinted)-(ActQty-QtyInserted)) AS N
FROM 
    [test].[dbo].[MM]
WHERE 
    DateDropped = 0
    AND (
    (ISNULL(@JobsOnHold, 0) = 1 AND DateAppr >= 0) 
    OR 
    (ISNULL(@JobsOnHold, 0) != 1 AND DateAppr != 0)
    )

You can read more about conditional WHERE here.

【讨论】:

随着表的增长,您需要观察最终查询的性能,因为优化器可能对这些类型的查询非常不满。 您可以使用@JobsOnHold IS NULLIS NOT NULL 而不是(ISNULL(@JobsOnHold, 0) = 1 @GaTechThomas,有什么解决办法吗? @shaijut 这类事情往往是需要不同方法的指标。考虑一个专门为手头的查询提供服务的物化视图,或者,如果您有灵活性,可以考虑将数据复制到一个单独的区域,从而为读取端提供更多选项(例如 CQRS)。【参考方案2】:

试试这个 -

WHERE DateDropped = 0
    AND (
        (ISNULL(@JobsOnHold, 0) = 1 AND DateAppr >= 0) 
        OR 
        (ISNULL(@JobsOnHold, 0) != 1 AND DateAppr != 0)
    )

【讨论】:

只是一个查询根据这个[link](dba.stackexchange.com/a/5337/27862) 答案可能会在 AND 运算符使用中解决这两个条件,所以如果第一个失败然后它也可以应用 DateAppr= 0 条件吗? OP 更改了代码 - 一个不那么微不足道的更改 - 让我们的答案看起来相当愚蠢:)【参考方案3】:

回答如何在 WHERE 子句中使用 CASE 表达式的基本问题:

首先要记住,CASE 表达式的值必须是普通数据类型的值,而不是布尔值。它必须是 varchar 或 int 或其他东西。这与您不能说 SELECT Name, 76 = Age FROM [...] 并期望在结果集中得到 'Frank', FALSE 的原因相同。

此外,WHERE 子句中的所有表达式都需要有一个布尔值。它们不能具有 varchar 或 int 的值。你不能说WHERE Name;WHERE 'Frank';。您必须使用比较运算符使其成为布尔表达式,所以WHERE Name = 'Frank';

这意味着 CASE 表达式必须位于布尔表达式的一侧。您必须将 CASE 表达式与某些东西进行比较。它不能自立!

这里:

WHERE 
    DateDropped = 0
    AND CASE
            WHEN @JobsOnHold  = 1 AND DateAppr >= 0 THEN 'True'
            WHEN DateAppr != 0 THEN 'True'
            ELSE 'False'
        END = 'True'

请注意左侧的 CASE 表达式最终如何将布尔表达式转换为 'True' = 'True''False' = 'True'

请注意,'False''True' 没有什么特别之处。如果您愿意,也可以使用01

您通常可以将 CASE 表达式重写为我们更熟悉的布尔表达式,这通常会提高性能。但是,有时使用现有表达式比转换逻辑更容易或更易于维护。

【讨论】:

【参考方案4】:

您的查询的问题在于,在 CASE 表达式中,THENELSE 部分必须有一个计算结果为数字或 varchar 或任何其他数据类型但不能计算为布尔值的表达式。

你只需要使用布尔逻辑(或者更确切地说是 SQL 使用的三元逻辑)并重写它:

WHERE 
    DateDropped = 0
AND ( @JobsOnHold = 1 AND DateAppr >= 0 
   OR (@JobsOnHold <> 1 OR @JobsOnHold IS NULL) AND DateAppr <> 0
    )

【讨论】:

只是一个查询根据这个 [link] (dba.stackexchange.com/a/5337/27862) 答案可能会在 AND 运算符使用中解决这两个条件,所以如果第一个失败,那么它也可以应用 DateAppr = 0 条件对吗? 不确定你在问什么。那里的问题/答案是关于短路的。你真的不应该担心这个——除非你有一些性能问题。【参考方案5】:

通常,当您使用条件 WHERE 子句时,您最终会得到一个非常低效的查询,这对于使用索引的大型数据集来说是显而易见的。 针对参数的不同值优化查询的一个好方法是为参数的每个值制定不同的执行计划。您可以使用OPTION (RECOMPILE) 实现此目的。

在这个例子中,它可能不会有太大的不同,但是如果说这个条件应该只用于两种情况之一,那么你会注意到一个很大的影响。

在这个例子中:

WHERE 
    DateDropped = 0
    AND (
    (ISNULL(@JobsOnHold, 0) = 1 AND DateAppr >= 0) 
    OR 
    (ISNULL(@JobsOnHold, 0) <> 1 AND DateAppr <> 0)
    )
OPTION (RECOMPILE)

来源Parameter Sniffing, Embedding, and the RECOMPILE Options

【讨论】:

【参考方案6】:

这似乎更容易考虑可以将两个参数中的任何一个传递到存储过程的位置。它似乎工作:

SELECT * 
FROM x 
WHERE CONDITION1
AND ((@pol IS NOT NULL AND x.PolicyNo = @pol) OR (@st IS NOT NULL AND x.State = @st))
AND OTHERCONDITIONS

【讨论】:

以上是关于SQL Server 中的条件 WHERE 子句的主要内容,如果未能解决你的问题,请参考以下文章

简述SELECT语句中的FROM、WHERE以及ORDER BY子句的作用。SQL Server

SQL HELP - 基于 BIT 变量的条件 where 子句 - SQL Server

SQL Server If 条件在 where 子句?

SQL Server 中带条件的 Where 子句

我需要从 SQL Server 查询中获取前 5 条记录,但要计算满足 where 子句条件的所有记录

如何在 SQL Server 2008 的 where 子句中仅比较日期并忽略时间