Предложение NOT HAVING (обратное HAVING) с агрегацией в Oracle - PullRequest
0 голосов
/ 19 февраля 2020

Я пытаюсь отфильтровать результаты GROUP BY CUBE с помощью предложения HAVING. Однако мне нужно сохранить строки, которые не соответствуют комбинации условий.

Я интуитивно пытался:

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?

Примечание: я нашел существующий вопрос, который был несколько связан, но он был указан c для Microsoft Access, и цель была не та, поэтому этот новый вопрос.

Ответы [ 4 ]

3 голосов
/ 19 февраля 2020

Математическое обратное к вашему предложению HAVING требует, чтобы вы изменили AND на OR и, если столбцы обнуляемы, также проверите нуль.

EG (если возможны нули):

HAVING (nvl(flag_1,1) != 1 OR NVL(flag_2,1) != 1 OR NVL(flag_3,1) != 1) 
2 голосов
/ 19 февраля 2020

Я думаю, что самое простое решение - просто изменить его на HAVING NOT ...

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

Но я часто нахожу NOT (...) неинтуитивным; в качестве альтернативы, ответ @ MichaelBroughton объясняет, как инвертировать логи c из NOT (x AND y) в NOT x OR NOT y

1 голос
/ 19 февраля 2020

Это то же самое, что и ответ Майкла Бротона, и я ненавижу дублировать, но я подумал, что это может быть яснее. Если он хочет включить что-либо из этого в свой ответ, я с радостью его удалю.

Чтобы определить логический эквивалент НЕ (boolean_expression), вы можете применить l aws Де Моргана. См .: https://en.wikipedia.org/wiki/De_Morgan%27s_laws

По сути, вы меняете логику c каждого термина и заменяете все И на ИЛИ, а все ИЛИ на И. Итак,

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)

Теперь примените первую часть l aws де Моргана и поменяйте логики c каждого термина. Так, например, 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)

Наконец, примените вторую часть l aws де Моргана и переключите все ANDs-> ORs и наоборот. ..

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 любое сравнение с нулем ложно. Итак,

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

Таким образом, строка с flag_1 и flag_2, равными 1, но flag_3 null, НЕ будет отображаться в ваших результатах.

0 голосов
/ 19 февраля 2020

Ну, я подумал об адаптации аналогичного метода на основе CASE, который я иногда использую внутри WHERE. Кажется, что 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 [...]
...