汇总表中找到的字段值的唯一组合

时间:2018-12-13 21:23:07

标签: sql oracle dbeaver

如何总结从2个字段的所有组合中返回多少记录,其中每个字段具有3个可能的值(YNNULL)?

目标是一个类似于以下的表,以及一个用于快速重新生成它的查询,以便我们可以验证清理工作。 (即受限制且明确不保密是无效状态,需要对那些记录进行调查)。重要的是要知道哪些组合返回零记录。没有在示例中提供所有组合是一种疏忽(并提供了一个为什么寻求非手动解决方案的好主意!)。

我知道如何单独获取结果表的每一行,但不能一次完成。源数据库是只读的,因此无法创建视图或新表。我正在使用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 |
+------------+--------------+-------------+--------------------------------------------------+

4 个答案:

答案 0 :(得分:2)

Oracle DB中有一个专门用于这种情况的工具。
这是GROUP BY子句的CUBE操作。我们可以在GROUPING函数的帮助下在此处使用它来区分 raw NULL值和代表摘要的NULL值。 (如果restrictedconfidential列不可为空,则查询会简单得多。)

它是这样的:

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一样)返回restrictedconfidential的所有组合,而仅返回实际在数据(和汇总)中出现的所有组合,因此要生成那些(零计数),则必须调整查询。
您列出了一些此类情况,例如:

+------------+--------------+-------------+--------------------------------------------------+
| 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 joingroup 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;