如何在 WHERE 子句中使用 CASE 语句和参数?

How to use CASE statement and a parameter in the WHERE clause?

我有一个 SSRS 报告,其中有一个参数要求用户包含收入大于零的记录,或收入值仅为零的记录。

由于查询不是存储过程并且不能将其放入过程中,因此我需要为嵌入式查询使用一些案例逻辑。最后我需要在 where 子句中执行此操作。

我正在尝试做这样的事情:

SELECT * FROM TABLE
WHERE MY_DATE BETWEEN D_START AND D_END
AND 
    CASE 
        WHEN :REVENUE = 1 THEN REV != 0
        WHEN :REVENUE = 2 THEN REV = 0
    END 

但是,当我 运行 这个查询时,我得到以下错误:

ORA-00905: missing keyword

难道我做的不可能吗?或者有没有人可以看到并帮助我解决的错误?

请帮忙。谢谢!

更新: 澄清一下,用户传递的值是 1 或 2。查询应该根据传递给它的值来过滤数据。如果参数传入1,则过滤掉所有不等于0的收入。否则,如果传递了两个,则进行过滤,以便仅返回收入为零的记录。

你可以用一点布尔逻辑写得更好:

SELECT * FROM TABLE
WHERE MY_DATE BETWEEN D_START AND D_END
AND (
      (:REVENUE = 1 AND REV != 0)
      OR
      (:REVENUE = 2 AND REV = 0 )
    )

CASE的意思是根据条件提取不同的值,所以你可以用它来检查条件,但你需要将它用作值来检查条件

不必使用 CASE 表达式来获得此特定结果。

但是可以利用一个。

原始查询中的问题是 Oracle 比其他数据库(如 MySQL)更严格,因为 Oracle 不会将 boolean 表达式隐式转换为,或将转换为布尔值

我怀疑 Oracle 在几个地方令人窒息。错误消息只向我们显示其中之一。

CASE 表达式 return 是一个 ,Oracle 犹豫不决,他不会将 计算为布尔值。

要将该值评估为布尔值,我们可以将该值与其他值进行比较

如果我们解决这个问题,我认为 Oracle 仍然会在 THEN 之后的表达式上卡住。 Oracle 期望 return 一个 ,它正在寻找一个比较,计算结果为布尔值。

好的,所以我们知道 CASE 表达式需要 return 一个值,我们需要在布尔表达式中使用它。如果我们将该条件测试移动到 WHEN 部分,并在 THEN 中指定要 return 的 ,我们可以比较 return 从 CASE 表达式到另一个值。

(顺便说一句...我强烈建议您限定 SQL 语句中的列引用。这使意图更加清晰。查看该语句,它看起来像 MY_DATE , D_START 和 D_END 都是列引用。这完全有效,对我来说似乎有点奇怪。)

例如,我们可以用 CASE 表达式做这样的事情:

SELECT t.*
 FROM TABLE t
WHERE t.MY_DATE BETWEEN t.D_START AND t.D_END
  AND CASE 
        WHEN ( :REVENUE = 1 AND t.REV != 0 ) THEN 1
        WHEN ( :REVENUE = 2 AND t.REV = 0  ) THEN 1
        ELSE NULL
      END = 1

CASE 中的括号不是必需的;我只是将它们包括在内以突出显示 Oracle 在 boolean 上下文中评估的部分。

那么,这行得通吗?如果为 :REVENUE 传入的值为 2,则第一个 WHEN 中的条件不会计算为 TRUE(第一次比较的结果保证为 FALSE)。第二个 WHEN 中的条件可能计算为 TRUE(第一次比较将产生 TRUE,第二次比较的结果将取决于 REV 列中的值。)

该 CASE 表达式将 return 值设为 1 或 NULL。 (如果需要,我们可以很容易地使用 0 或 -1 或 999 代替 NULL。)

一旦计算了 CASE 表达式, returned 将与文字值进行比较,就好像我们写了例如val = 1。该比较被评估为布尔值。如果计算结果为 TRUE,则该行将 returned...

为了使 Oracle 的行为与其他数据库(如 MySQL)相似,我们需要明确地进行布尔值到值和值到布尔值的转换。与 1 相比,我们仍然需要 CASE 中的 return,就像我们上面所做的那样。我们可以使用另一个 CASE 表达式来代替 REV != 0。我不推荐这个,这里只是为了说明,将 boolean 转换为 value.

 WHERE CASE 
         WHEN ( :REVENUE = 1 )
           THEN    CASE WHEN  ( t.REV != 0 ) THEN 1 ELSE NULL END
         WHEN ( :REVENUE = 2 )
           THEN    CASE WHEN  ( t.REV = 0  ) THEN 1 ELSE NULL END
         ELSE
           NULL
       END = 1

请注意,最外层 CASE 表达式中的 return 正在与一个值进行比较,因此我们得到一个布尔值(Oracle 需要一个布尔值。)

以上语句中的所有 ELSE NULL 都可以省略以获得等效结果,因为这是省略 ELSE 时的默认值。)


同样,没有必要使用 CASE 表达式。没有它,您可以获得相同的结果。例如:

  SELECT t.*
    FROM TABLE t
   WHERE t.MY_DATE BETWEEN t.D_START AND t.D_END
     AND (  ( :REVENUE = 1 AND t.REV != 0 ) 
        OR  ( :REVENUE = 2 AND t.REV = 0 )
         )

在所有 return 相同结果的这些查询中,CASE 表达式不会给我们带来任何好处。但在某些情况下,它比常规 OR 有一些优势,因为 CASE 表达式在 WHEN 子句中的条件计算为 TRUE.[= 时停止计算。 30=]

问题是 Oracle SQL 没有布尔数据类型,所以你不能有布尔类型的列,不能将布尔参数传递给查询,不能有布尔表达式等。所以它们有点不自然"condition" 的概念是进入逻辑条件的东西(比如在 WHERE 子句中)。不幸的是,当他们引入 case EXPRESSION 时,它可以在任何其他表达式可以使用的地方使用(但这不包括布尔值),他们没有引入 "case CONDITION" - 它可以在可以使用其他条件的地方使用。这种遗漏很奇怪,因为 case 条件的代码可能会使用 95% 的 case 表达式代码。更奇怪的是 PL/SQL 确实具有布尔类型,并且那里的 case 表达式可以无缝地用于布尔值。