如何总结从2个字段的所有组合中返回多少记录,其中每个字段具有3个可能的值(Y
,N
,NULL
)?
目标是一个类似于以下的表,以及一个用于快速重新生成它的查询,以便我们可以验证清理工作。 (即受限制且明确不保密是无效状态,需要对那些记录进行调查)。重要的是要知道哪些组合返回零记录。没有在示例中提供所有组合是一种疏忽(并提供了一个为什么寻求非手动解决方案的好主意!)。
我知道如何单独获取结果表的每一行,但不能一次完成。源数据库是只读的,因此无法创建视图或新表。我正在使用Dbeaver进行分析。
SELECT x.*,x.ROWID FROM table.PROJECT x
WHERE CONFIDENTIAL = 'Y'
然后
SELECT x.*,x.ROWID FROM table.PROJECT x
WHERE CONFIDENTIAL = 'Y' AND RESTRICTED = NULL
...等
所需的结果表(不需要“描述”,仅为上下文显示):
+------------+--------------+-------------+--------------------------------------------------+ | RESTRICTED | CONFIDENTIAL | Num records | Description | +------------+--------------+-------------+--------------------------------------------------+ | {any} | {any} | 586 | any status, i.e. "all records" | | | | | | | Y | {any} | 191 | Restricted, assumed not confidential | | Y | N | 184 | Restricted, not confidential | | Y | Y | 7 | Restricted, is confidential | | Y | NULL | 0 | Restricted, assumed not confidential | | | | | | | N | {any} | 395 | Not restricted, any confidential | | N | N | 329 | Not restricted, not confidential | | N | Y | 7 | Not restricted, is confidential | | N | NULL | 0 | Not restricted, assumed not confidential | | | | | | | {any} | N | 513 | Assumed not restricted, not confidential | | {any} | Y | 7 | Assumed not restricted, is confidential | | {any} | NULL | 66 | Assumed not restricted, assumed not confidential | | | | | | | NULL | {any} | 0 | Assumed not restricted, assumed not confidential | +------------+--------------+-------------+--------------------------------------------------+
答案 0 :(得分:2)
Oracle DB中有一个专门用于这种情况的工具。
这是GROUP BY
子句的CUBE
操作。我们可以在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 DB将能够在一个具有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)
但是它将列出所有零行,而不仅仅是列出的行。
答案 1 :(得分:2)
在分组中使用CUBE
:
Oracle设置:
CREATE TABLE project ( restricted, confidential ) AS
SELECT 'Y', 'Y' FROM DUAL CONNECT BY LEVEL <= 7 UNION ALL
SELECT 'Y', 'N' FROM DUAL CONNECT BY LEVEL <= 184 UNION ALL
SELECT 'Y', NULL FROM DUAL WHERE 1 = 0 UNION ALL
SELECT 'N', 'Y' FROM DUAL CONNECT BY LEVEL <= 7 UNION ALL
SELECT 'N', 'N' FROM DUAL CONNECT BY LEVEL <= 329 UNION ALL
SELECT 'N', NULL FROM DUAL CONNECT BY LEVEL <= 59 UNION ALL
SELECT NULL, 'Y' FROM DUAL CONNECT BY LEVEL <= 1 UNION ALL
SELECT NULL, 'N' FROM DUAL CONNECT BY LEVEL <= 2 UNION ALL
SELECT NULL, NULL FROM DUAL WHERE 1 = 0;
查询:
SELECT CASE GROUPING( restricted )
WHEN 1 THEN 'any'
ELSE restricted
END AS restricted,
CASE GROUPING( confidential )
WHEN 1 THEN 'any'
ELSE confidential
END AS confidential, COUNT(*)
FROM project
GROUP BY CUBE( restricted, confidential )
ORDER BY 1 NULLS FIRST, 2 NULLS FIRST;
输出:
RESTRICTED CONFIDENTIAL COUNT(*)
---------- ------------ --------
- N 2
- Y 1
- any 3
N - 59
N N 329
N Y 7
N any 395
Y N 184
Y Y 7
Y any 191
any - 59
any N 515
any Y 15
any any 589
查询2 :要获取所有组合,
WITH options ( value ) AS (
SELECT 'Y' FROM DUAL UNION ALL
SELECT 'N' FROM DUAL UNION ALL
SELECT NULL FROM DUAL
)
SELECT CASE GROUPING( r.value )
WHEN 1 THEN 'any'
ELSE r.value
END AS restricted,
CASE GROUPING( c.value )
WHEN 1 THEN 'any'
ELSE c.value
END AS confidential,
COUNT( p.n )
FROM options r
CROSS JOIN options c
LEFT OUTER JOIN ( SELECT p.*, 1 AS n FROM project p ) p
ON ( p.restricted = r.value OR ( p.restricted IS NULL AND r.value IS NULL ) )
AND ( p.confidential = c.value OR ( p.confidential IS NULL AND c.value IS NULL ) )
GROUP BY CUBE( r.value, c.value )
ORDER BY 1 NULLS FIRST, 2 NULLS FIRST;
输出:
RESTRICTED CONFIDENTIAL COUNT(P.N)
---------- ------------ ----------
- - 0
- N 2
- Y 1
- any 3
N - 59
N N 329
N Y 7
N any 395
Y - 0
Y N 184
Y Y 7
Y any 191
any - 59
any N 515
any Y 15
any any 589
答案 2 :(得分:0)
这不是普通的汇总吗?例如:
SQL> with project (id, confidential, restricted) as
2 (select 1, 'y', null from dual union all
3 select 2, 'n', null from dual union all
4 select 3, null, 'y' from dual union all
5 select 4, null, null from dual union all
6 select 5, 'n', 'n' from dual union all
7 select 6, 'y', 'y' from dual union all
8 select 7, 'y', 'n' from dual union all
9 select 8, 'y', 'y' from dual
10 )
11 select restricted, confidential, count(*)
12 from project
13 group by restricted, confidential;
R C COUNT(*)
- - ----------
1
y y 2
y 1
n y 1
n 1
y 1
n n 1
7 rows selected.
SQL>
答案 3 :(得分:0)
使用cross join
生成行,然后使用left join
和group by
计算计数:
with vals as (
select 'Y' as val from dual union all
select 'N' as val from dual union all
select NULL as val from dual
)
select vc.val as confidential, vr.val as restricted,
count(p.rowid)
from vals vc cross join
vals vr left join
table p
on (p.confidential = vc.val or p.confidential is null and vc.val is null) and
(p.restricted = vr.val or p.restricted is null and vr.val is null)
group by vc.val, vr.val;