NOT HAVING (inverse HAVING) 子句与 Oracle 中的聚合

NOT HAVING (inverse HAVING) clause with aggregation in Oracle

我正在尝试使用 HAVING 子句过滤 GROUP BY CUBE 的结果。但是我需要保留不满足条件组合的行。

我凭直觉尝试过:

SELECT [...]
FROM [...]
NOT HAVING (flag_1 = 1 AND flag_2 = 1 AND flag_3 = 1)
GROUP BY CUBE [...]

遗憾的是 Oracle 无法将 NOT HAVING 识别为有效语法。

从数学的角度来看,反转每个单独的条件不会产生相同的结果:

HAVING (flag_1 != 1 AND flag_2 != 1 AND flag_3 != 1)

如何实现 NOT HAVING 的逻辑等效?

注意:我发现了一个有点相关的现有问题,但它特定于 Microsoft Access,目标也不相同,因此提出了这个新问题。

好吧,我考虑过根据我有时在 WHERE 中使用的基于 CASE 的类似方法进行改编。 CASE 似乎也在 HAVING 内工作!

解决方案:

SELECT [...]
FROM [...]

HAVING
    CASE
        WHEN flag_1 = 1 AND flag_2 = 1 AND flag_3 = 1
        THEN 0
        ELSE 1
    END = 1

GROUP BY CUBE [...]

您的 HAVING 子句的数学逆运算要求您将 AND 更改为 OR,并且如果列可以为空,还需要进行空值检查。

EG(如果可能为空):

HAVING (nvl(flag_1,1) != 1 OR NVL(flag_2,1) != 1 OR NVL(flag_3,1) != 1) 

我认为最简单的解决方案就是将其更改为 HAVING NOT ...

SELECT [...]
FROM [...]
HAVING NOT (flag_1 = 1 AND flag_2 = 1 AND flag_3 = 1)
GROUP BY CUBE [...]

但我经常发现 NOT (...) 不直观;作为替代方案,@MichaelBroughton 的回答解释了如何将逻辑从 NOT (x AND y) 反转为 NOT x OR NOT y

这与 Michael Broughton 的回答相同,我不想重复,但我认为它可以更清楚。如果他想将其中的任何内容合并到他的答案中,我很乐意删除这个。

要确定 NOT (boolean_expression) 的逻辑等价物,您可以应用德摩根定律。参见:https://en.wikipedia.org/wiki/De_Morgan%27s_laws

基本上,您将每个术语的逻辑颠倒过来,将所有 AND 更改为 OR,将所有 OR 更改为 AND。所以,

NOT (A AND B AND C) ==> (NOT A OR NOT B OR NOT C)

但是您也需要跟踪空值。过程如下:

开始于...

HAVING NOT (flag_1 = 1 AND flag_2 = 1 AND flag_3 = 1)

首先,添加关于起始表达式中存在的 NULL 的隐式假设。例如,如果 flag_1=1,那么它当然不是 NULL。

HAVING NOT (flag_1 = 1 AND flag_1 IS NOT NULL 
        AND flag_2 = 1 AND flag_2 IS NOT NULL 
        AND flag_3 = 1 AND flag_3 IS NOT NULL)

现在,应用德摩根定律的第一部分并反转每一项的逻辑。因此,例如,flag_1 = 1 变为 flag_1 != 1...

HAVING (flag_1 != 1 AND flag_1 IS NULL 
    AND flag_2 != 1 AND flag_2 IS NULL 
    AND flag_3 != 1 AND flag_3 IS NULL)

最后,应用 De Morgan 定律的第二部分并切换所有 AND->OR,反之亦然...

HAVING (flag_1 != 1 OR flag_1 IS NULL 
     OR flag_2 != 1 OR flag_2 IS NULL 
     OR flag_3 != 1 OR flag_3 IS NULL)

这就是你的答案。

NOT (flag_1 = 1 AND flag_2 = 1 AND flag_3 = 1) 怎么样?

这是行不通的,因为它没有像人们预期的那样处理空值。在 Oracle 中,任何与 null 的比较都是错误的。所以,

    (1 = NULL) ... false
NOT (1 = NULL) ... also false

因此 flag_1flag_2 都等于 1 但 flag_3 null 的行不会显示在您的结果中。