【问题标题】:How to use CASE statement and a parameter in the WHERE clause?如何在 WHERE 子句中使用 CASE 语句和参数?
【发布时间】:2016-04-01 20:11:11
【问题描述】:

我有一个 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的收益。否则,如果通过了两个,则进行过滤,以便仅返回收入为零的记录。

【问题讨论】:

  • 可以在 WHERE 子句中使用 CASE 表达式和参数。我提供了几个示例来证明这一点,并解释了为什么 Oracle 对原始语句的语法犹豫不决。 (选择的答案似乎暗示在 WHERE 子句中不能使用 CASE 表达式。)
  • @spencer7593:看起来我确实排除了 WHERE 条件的 CASE;已编辑,谢谢。 Zolt:公平地说,公认的答案应该是斯宾塞的,比我的要好很多
  • @Aleksej:OP 应该选择最适合他需要的答案。正如您的回答(正确)指出的那样,OP 在 WHERE 子句中不需要 CASE 表达式。我的回答远远超出了这个范围,并解释了为什么 Oracle 在 OP 查询中犹豫不决(找到一个 condition 它需要一个 value 和一个 value 它需要一个条件。(我的回答是针对标题中更一般的问题...“如何在 WHERE 子句中使用 CASE 表达式和参数”。

标签: oracle parameters case where-clause


【解决方案1】:

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

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是为了根据条件提取不同的值,所以你可以用它来检查条件,但是你需要用它作为一个值来检查一个条件

【讨论】:

  • @Zolt :您在问题中的编辑应该已经可以回答了
  • @Aleksej:您的答案是最能回答 OP 所问问题的答案。这是 OP 真正想做的事情。 CASE 表达式的妙处(特别是在使用 WHERE 子句方面)是短路评估。一旦 WHEN 条件评估为 TRUE,则不会检查任何其他 WHEN 条件。对于检查许多不一定互斥的条件,它可以让我们不必表达,作为后续 OR 条件的一部分......“并且之前的 OR 条件都不是真的”。
【解决方案2】:

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

但是可以使用一个。

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

我怀疑甲骨文在几个地方窒息。错误消息仅向我们显示其中之一。

CASE 表达式返回一个 value,Oracle 不愿将 value 评估为布尔值。

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

如果我们解决了这个问题,我认为 Oracle 仍然会被 THEN 后面的表达式窒息。 Oracle 期望返回一个 ,并且它正在寻找一个比较,它的计算结果是一个布尔值。

好的,所以我们知道CASE 表达式需要返回一个值,我们需要在布尔表达式中使用它。如果我们将该条件测试移到WHEN 部分,并在THEN 中指定要返回的,我们可以将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 表达式将返回值 1 或 NULL。 (如果需要,我们可以很容易地使用 0 或 -1 或 999 来代替 NULL。)

一旦 CASE 表达式被求值,返回的 value 将与字面值进行比较,就像我们写了 e.g. val = 1。该比较被评估为布尔值。如果计算结果为 TRUE,则将返回该行...

为了让 Oracle 的行为与其他数据库(如 MySQL)相似,我们需要进行从布尔值到值的转换以及从值到布尔值的显式转换。与 1 相比,我们仍然需要 CASE 的回报,就像我们上面所做的那样。代替REV != 0,我们可以使用另一个CASE 表达式。我不推荐这样做,这里只是为了说明,将 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 表达式的返回值正在与一个值进行比较,因此我们得到一个布尔值(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 )
         )

在这些都返回等效结果的查询中,CASE 表达式不会给我们带来任何好处。但在某些情况下,它可能比常规 OR 有一些优势,因为当 WHEN 子句中的条件计算为 TRUE 时,CASE 表达式会停止计算。

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 对,Oracle 记录了可以由 CASE 表达式返回的数据类型... CHAR、VARCHAR2、NUMBER、DATE 等。该返回类型列表不包括“条件”。 (在我的回答中,我反复使用了“布尔”和“比较”这两个术语。正如您正确指出的那样,Oracle 文档将其称为“条件”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-14
    • 2022-01-07
    • 2019-07-14
    • 1970-01-01
    相关资源
    最近更新 更多