如何在 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 表达式可以无缝地用于布尔值。
我有一个 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 表达式可以无缝地用于布尔值。