Специально для этого сценария в Oracle DB имеется инструмент.
Это операция CUBE
предложения GROUP BY
.Мы можем использовать его здесь с помощью функции GROUPING
, чтобы различать значение raw NULL
и значение NULL
, представляющее сводку.(Запрос был бы намного проще, если бы столбцы restricted
и confidential
не были бы обнуляемыми.)
Это выглядит так:
SELECT DECODE(GROUPING(t.restricted), 1, '{any}', t.restricted) AS restricted,
DECODE(GROUPING(t.confidential), 1, '{any}', t.confidential) AS confidential,
COUNT(*)
FROM project t
GROUP BY CUBE(t.restricted, t.confidential);
Некоторое объяснение:
Использование CUBE
изменяет поведение GROUP BY
, чтобы не только выполнять агрегации для групп, определенных по заданным столбцам, но также и создавать сводные группы (в основном путем исключения всех комбинаций столбцов из критериев группировки, включая исключение всех, для итоговой сводки).
Группам сводок будет присвоено значение NULL
в столбцах, исключенных из сводки.Если этот столбец может иметь значение NULL
, то БД Oracle сможет различать обычную группу со значением NULL
и сводную группу.Вы можете получить доступ к этой информации, используя функцию GROUPING
, которая вернет 1, если это сводная группа для столбца, переданного этой функции, и 0 в противном случае.
Редактировать:
Важное замечание: запрос не будет (так же, как с обычным GROUP BY
) возвращать все комбинации restricted
и confidential
, только те, которые фактически встречаются в данных (и сводках), поэтому производить их (с нулем)считать), вам необходимо настроить запрос.
Вы перечислили некоторые из таких случаев, например:
+------------+--------------+-------------+--------------------------------------------------+
| RESTRICTED | CONFIDENTIAL | Num records | Description |
+------------+--------------+-------------+--------------------------------------------------+
| Y | NULL | 0 | Restricted, assumed not confidential |
| N | NULL | 0 | Not restricted, assumed not confidential |
| NULL | {any} | 0 | Assumed not restricted, assumed not confidential |
+------------+--------------+-------------+--------------------------------------------------+
, но не все, например:
+------------+--------------+-------------+
| RESTRICTED | CONFIDENTIAL | Num records |
+------------+--------------+-------------+
| NULL | N | 0 |
| NULL | Y | 0 |
| NULL | NULL | 0 |
+------------+--------------+-------------+
Редактировать 2:
Чтобы покрыть нулевые значения в результате, вы можете попробовать что-то вроде этого (обратите внимание SUM
на пользовательский столбец, заменяющий COUNT
строк):
WITH v AS (
SELECT 'Y' AS val FROM DUAL UNION ALL
SELECT 'N' AS val FROM DUAL UNION ALL
SELECT NULL AS val FROM DUAL
),
s AS (
SELECT id, restricted, confidential, 1 AS cnt
FROM project
UNION ALL
SELECT NULL, r.val, c.val, 0
FROM v r
CROSS JOIN v c
)
SELECT DECODE(GROUPING(s.restricted), 1, '{any}', s.restricted) AS restricted,
DECODE(GROUPING(s.confidential), 1, '{any}', s.confidential) AS confidential,
SUM(s.cnt) AS cnt
FROM s
GROUP BY CUBE(s.restricted, s.confidential)
Однако в нем будут перечислены все нулевые строки, а не только те, которые вы перечислили, как ожидалось.