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

时间:2016-04-01 20:11:12

标签: oracle parameters case 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,则过滤掉所有不等于零的收入。否则,如果两个传递,则过滤,以便仅返回收入为零的记录。

3 个答案:

答案 0 :(得分:2)

您可以使用一些布尔逻辑更好地编写它:

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用于根据条件提取不同的值,因此您可以使用它来检查条件,但您需要将其用作检查条件的值

答案 1 :(得分:2)

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

但是可以使用一个。

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

我怀疑甲骨文在几个地方窒息。错误消息只向我们展示了其中一个。

CASE表达式返回,而甲骨文则不愿意将作为布尔值进行评估。

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

如果我们解决这个问题,我认为甲骨文仍然会在THEN之后扼杀表达式。 Oracle希望返回一个,并且它会找到一个比较,它会计算为布尔值。

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

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

例如,我们可以使用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中的parens是不必要的;我只是将它们包含在内以突出显示Oracle在布尔上下文中评估的部分。

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

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

评估CASE表达式后,返回的将与文字值进行比较,就像我们写的那样val = 1。该比较被评估为布尔值。如果它的计算结果为TRUE,则返回行...

为了使Oracle的行为与其他数据库(如MySQL)类似,我们需要将布尔值转换为值,将值转换为布尔显式。我们仍然需要CASE的回报与1相比,就像我们上面所做的那样。代替REV != 0我们可以使用另一个CASE表达式。我不推荐这个,只是在这里显示,将布尔转换为

 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具有一些优势,因为当CASE子句中的条件求值为WHEN时,TRUE表达式会停止求值。

答案 2 :(得分:0)

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