【问题标题】:Summarize unique combination of field values found in table汇总表中找到的字段值的唯一组合
【发布时间】:2018-12-13 21:23:07
【问题描述】:

如何总结 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

...等

所需的结果表(不需要有“描述”,仅显示上下文):

+------------+--------------+-------------+------- -------------------------------------------------------+ |受限 |机密 |记录数 |说明 | +------------+--------------+-------------+------- -------------------------------------------------------+ | {任何} | {任何} |第586章任何状态,即“所有记录” | | | | | | |是 | {任何} | 191 |受限,假定不保密 | |是 | N | 184 |限制,不保密 | |是 |是 | 7 |受限,保密 | |是 |空 | 0 |受限,假定不保密 | | | | | | | N | {任何} | 395 |不受限制,任何机密 | | N | N | 329 |不受限制,不保密 | | N |是 | 7 |不受限制,保密 | | N |空 | 0 |不受限制,假定不保密 | | | | | | | {任何} | N | 513 |假定不受限制,不保密 | | {任何} |是 | 7 |假定不受限制,属于机密| | {任何} |空 | 66 |假定不受限制,假定不保密 | | | | | | |空 | {任何} | 0 |假定不受限制,假定不保密 | +------------+--------------+-------------+------- -------------------------------------------------------+

【问题讨论】:

  • 对于所需的输出,您还显示了一些零记录组合(例如,YNULLNNULLNULL{any}),但不是全部(例如 NULLYNNULL)。这种对零行计数的选择性处理是您目标的重要组成部分吗?
  • @Hilarion 是的,知道哪些组合返回零记录很重要。示例中没有出现所有组合是一种疏忽(并提供了一个演示,说明为什么寻求非手动解决方案是一个好主意!)。
  • 感谢您的澄清。我建议编辑问题,以更正所需的结果,以便所有新贡献者都可以轻松解决整个问题。
  • @Hilarion,所以添加了。
  • 观察者注意:我已经离开这个项目几天了,到目前为止无法评估答案。我会尽快回来。

标签: sql oracle dbeaver


【解决方案1】:

在分组中使用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

【讨论】:

  • 您的输出中似乎缺少总摘要行(anyany)。查询本身似乎很好。
  • @Hilarion 谢谢,复制/粘贴时错过了输出的最后一行。
【解决方案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)

但它会列出所有零行,而不仅仅是您按预期列出的那些。

【讨论】:

    【解决方案3】:

    普通的聚合不能完成这项工作吗?例如:

    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>
    

    【讨论】:

    • 这似乎没有计算摘要,例如无论restricted 的值如何(即问题中给出的所需输出中的“{any}”行),也不会计算confidential 的“Y”行数。
    【解决方案4】:

    使用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;
    

    【讨论】:

    • 这似乎没有计算摘要,例如也不会计算confidential 的“Y”有多少行,无论restricted 值如何(即问题中给出的所需输出中的“{any}”行。
    猜你喜欢
    • 1970-01-01
    • 2022-08-22
    • 1970-01-01
    • 2011-06-09
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多