没有必要使用 CASE 表达式来获得此特定结果 .
但是可以使用一个 .
原始查询中的问题是Oracle比其他数据库(如MySQL)更严格,因为Oracle不会将布尔表达式隐式转换为值,或将值转换为布尔值 .
我怀疑甲骨文在几个地方窒息 . 错误消息只向我们展示其中之一 .
CASE表达式返回一个值,而Oracle则不愿意将该值作为布尔值进行评估 .
要将该值评估为布尔值,我们可以将值与其他值进行比较 .
如果我们解决这个问题,我认为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中的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 时停止求值 .