【问题标题】:Switch case/If in a where clause在 where 子句中切换 case/If
【发布时间】:2011-05-27 14:53:51
【问题描述】:

我正在尝试根据某个值更改 where 部分的条件。我在这里看过类似的文章,包括:

SQL Switch/Case in where clause

但他们并没有完全处理我的案子。

我所拥有的是一个基于其输入进行选择的存储过程。这些输入是可选的(或者具有他们喜欢调用的默认参数)。

所述参数中有2个是数字。

  • 如果仅指定第一个数字,则 X 列必须等于该数字。
  • 如果指定了第一个和第二个数字,则 X 列必须 >= 到第一个 AND
  • 如果只指定了第二个数字,则它是无效的。

这是我尝试过的(显然没有用)

DECLARE @SECOND INT;
DECLARE @FIRST INT;

SET @FIRST = 123456;
SET @SECOND = 67890;

SELECT * FROM BANK_DETAIL 
WHERE -- POSSIBLY SOME OTHER WHERE CLAUSES
    CASE
        WHEN @SECOND IS NULL THEN 
            X = @FIRST
        ELSE 
            X >= @FIRST AND X <= @SECOND 
    END
        -- POSSIBLY SOME MORE WHERE CLAUSES
ORDER BY X

真的这感觉就像它需要一个 IF/ELSE 而不是 CASE 但我是针对 CASE .....

哦,这是 MS SQL >= 2005

【问题讨论】:

标签: sql sql-server sql-server-2005 tsql stored-procedures


【解决方案1】:

尝试以 @SECOND 的无效性为条件,而不是使用 CASE。

SELECT *
FROM BANK_DETAIL
WHERE
-- other conditions go here
AND ((@SECOND IS NULL AND X = @FIRST)
  OR (@SECOND IS NOT NULL AND X >= @FIRST AND X <= @SECOND))

【讨论】:

  • +1 用于将整个内容封装在括号中,但我认为您不需要 IS NOT NULL
  • 任何与 NULL 的比较都不会评估为假。它的计算结果为 NULL。
  • 如果我更习惯三值逻辑,我可能会消除 IS NOT NULL 条件。但对我来说,把它留在里面似乎更清楚。也许一年后我会有不同的意见......
【解决方案2】:
SELECT * FROM BANK_DETAIL 
WHERE
    (@SECOND IS NULL AND X = @FIRST) or
    (X >= @FIRST AND X <= @SECOND)

虽然这样做会更有效率:

IF @SECOND IS NULL
BEGIN
   SELECT * FROM BANK_DETAIL 
   WHERE
     X = @FIRST
END
ELSE
BEGIN
   SELECT * FROM BANK_DETAIL 
   WHERE
     X >= @FIRST AND X <= @SECOND
END

【讨论】:

  • 取决于缓存,我不认为你的第二种方法总是会更高效(并且还会导致重复代码的维护问题)
  • @Tom:这取决于计划者。我不熟悉 SQL-Server 的内部结构,但 Postgres 会以这样的方式准备第一个查询,即它会执行两次位图索引扫描(导致重新检查条件,其中一个不一定需要),它会准备接下来的两个,以便他们进行正常的索引扫描。
  • 在范围内搜索与搜索特定值是不同的前景。当然,这取决于表中的数据、统计信息和索引。根据经验,OR 子句在 WHERE 子句的顶层通常不是那么好——根据我的经验。
【解决方案3】:

我会使用布尔运算符:

SELECT * FROM BANK_DETAIL 
WHERE
  ((@SECOND IS NULL AND X = @FIRST) OR 
  (@SECOND IS NOT NULL AND X >= @FIRST AND X <= @SECOND));

【讨论】:

    【解决方案4】:

    您正在构建一个dynamic search condition。通过强制一个语句覆盖 both 情况,您正在削减优化器选项。当@seconds 为空和不为空时,生成的计划必须在两种 情况下工作。使用两个单独的语句会更好:

    IF @SECOND IS NULL THEN
       SELECT * FROM BANK_DETAIL 
       WHERE  X = @FIRST
       ORDER BY X
    ELSE
       SELECT * FROM BANK_DETAIL 
       WHERE X >= @FIRST AND X <= @SECOND 
       ORDER BY X
    

    您将“简化”为一个单一语句的直觉会导致您走错路。结果是更少的文本,但由于次优计划导致执行时间更多。我的回复开头链接的文章非常详细地介绍了这个主题。

    【讨论】:

      【解决方案5】:

      问题在于CASE 返回一个值,它不是逻辑分支。 OMG 提供的链接几乎是该链接的权威来源(Erland Sommarskog 提供的几乎所有内容都是很好的建议)。

      链接的快速摘要:

      您可以使用动态 SQL,根据条件构建语句。这通常是性能最好的方法,但也有缺点。最大的缺点之一是可能存在的安全问题,因此请确保您完全了解 SQL 注入攻击以及如何防止它们。

      另一种方法是使用 OR 在 WHERE 语句中使用复杂的逻辑。在您的情况下,它将如下所示。这种方法比动态 SQL 更简单,更安全,但性能可能并不总是很好。如果性能适合您的情况(测试它),那么请坚持使用这种方法。

      SELECT
          *    -- I assume that you used * just as an example and don't actually use this in your production code
      FROM
          Bank_Detail
      WHERE -- POSSIBLY SOME OTHER WHERE CLAUSES
          (@second IS NULL AND X = @first) OR
          (@second IS NOT NULL AND (x >= @first AND x <= @second))
          -- POSSIBLY SOME MORE WHERE CLAUSES
      ORDER BY
          x
      

      我刚刚想到了另一种组织语句的方式......

      SELECT
          *
      FROM
          Bank_Detail
      WHERE -- POSSIBLY SOME OTHER WHERE CLAUSES
          x >= @first AND
          x <= COALESCE(@second, @first)
          -- POSSIBLY SOME MORE WHERE CLAUSES
      ORDER BY
          x
      

      我还没有测试过,但我认为这在逻辑上是等效的,并且可以为您提供更一致的查询计划。

      Erland 还提供了其他几种可能的方法,因此请务必阅读他关于该主题的完整文章。

      【讨论】:

      • 虽然第二个版本有一些吸引力,但我相信它包含不需要的行,其中x &gt; @first@second is null
      • 当@second 为空时,where 子句简化为x &gt;= @first AND x &lt;= @first - 这相当于x = @first,根据需要。
      猜你喜欢
      • 1970-01-01
      • 2014-11-20
      • 1970-01-01
      • 2019-07-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-24
      • 1970-01-01
      相关资源
      最近更新 更多