Oracle SQL - 如何动态获取每列的非重复计数?

时间:2018-03-05 15:33:05

标签: sql oracle

我发现的类似问题似乎没有具体回答如何在不查询查询中的每一列的情况下对所有列的不同元素进行单独计数。

我希望对所有列执行类似的操作,但不要键入每列:

SELECT     
  COUNT(distinct COL1) AS COL1DISTINCT,     
  COUNT(distinct COL2) AS COL2DISTINCT    
FROM TABLE_NAME;

但是我们不想输入所有单独的列,因为需要在几个不同的客户端上的几个其他表上检索相同的数据,每次都会有不同的列名。

我尝试了一种访问列名来创建子查询的方法,但是我们的元数据表是空的,并且不提供任何列名。

1 个答案:

答案 0 :(得分:1)

如果您确定每列有一个结果集行,则可以调整this XML magic trick

select owner, table_name, column_name,
  to_number(xmlquery('/ROWSET/ROW/C/text()'
    passing xmltype(dbms_xmlgen.getxml(
      'select count(distinct "' || column_name || '") as c '
      || 'from "' || owner || '"."' || table_name || '"'))
  returning content)) as c
from all_tab_columns
where owner = '<your table owner>'
and data_type in ('NUMBER', 'DATE', 'TIMESTAMP', 'CHAR', 'VARCHAR2', 'NCHAR', 'NVARCHAR2');

...列出您需要计算的所有数据类型;真的是排除那些不能像CLOB一样处理distinct的东西,但是你可能已经嵌套了表等等,它可能会更简单到列出你想要并期望能够计算的那些。

dbms_xmlgen()调用将动态有效构造的select count(distinct ...) ...查询的结果转换为XML结构,然后您可以将计数从XMLQuery()中拉出(而不是链接答案中已弃用的extractvalue()

作为一个非常快速的演示:

create table t42 (id number, str varchar2(20));
insert into t42 values (1, 'Test');
insert into t42 values (2, 'Test');
insert into t42 values (3, 'Test 2');
insert into t42 values (3, null);

select owner, table_name, column_name,
  to_number(xmlquery('/ROWSET/ROW/C/text()'
    passing xmltype(dbms_xmlgen.getxml(
      'select count(distinct "' || column_name || '") as c '
      || 'from "' || owner || '"."' || table_name || '"'))
  returning content)) as c
from all_tab_columns
where owner = 'MY_SCHEMA'
and table_name = 'T42'
and data_type in ('NUMBER', 'DATE', 'TIMESTAMP', 'CHAR', 'VARCHAR2', 'NCHAR', 'NVARCHAR2');

OWNER           TABLE_NAME      COLUMN_NAME              C
--------------- --------------- --------------- ----------
MY_SCHEMA       T42             ID                       3
MY_SCHEMA       T42             STR                      2
  

是否还有一种方法可以在每列中获取空值计数?

count()函数会忽略空值,因此要计算那些必须转换它们的函数,例如与

count(case when <your_column> is null then 1 end)

你在这里包含第二个XMLQuery子句:

select owner, table_name, column_name,
  to_number(xmlquery('/ROWSET/ROW/C/text()'
    passing xmltype(dbms_xmlgen.getxml(
      'select count(distinct "' || column_name || '") as c '
      || 'from "' || owner || '"."' || table_name || '"'))
  returning content)) as distinct_count,
  to_number(xmlquery('/ROWSET/ROW/C/text()'
    passing xmltype(dbms_xmlgen.getxml(
      'select count(case when "' || column_name || '" is null then 1 end) as c '
      || 'from "' || owner || '"."' || table_name || '"'))
  returning content)) as null_count
from all_tab_columns
where owner = 'MY_SCHEMA'
and table_name = 'T42'
and data_type in ('NUMBER', 'DATE', 'TIMESTAMP', 'CHAR', 'VARCHAR2',
    'NCHAR', 'NVARCHAR2');

OWNER           TABLE_NAME      COLUMN_NAME     DISTINCT_COUNT NULL_COUNT
--------------- --------------- --------------- -------------- ----------
MY_SCHEMA       T42             ID                           3          0
MY_SCHEMA       T42             STR                          2          1

或使用单个XMLTable从生成的XML中提取两个列值,并将其修改为一次执行两个计数:

select a.owner, a.table_name, a.column_name,
  x.distinct_count, x.null_count
from
(
  select owner, table_name, column_name,
    dbms_xmlgen.getxml(
      'select count(distinct "' || column_name || '") as c1,'
        || 'count(case when "' || column_name || '" is null then 1 end) as c2 '
      || 'from "' || owner || '"."' || table_name || '"') as xml_clob
  from all_tab_columns
  where owner = 'MY_SCHEMA'
  and table_name = 'T42'
  and data_type in ('NUMBER', 'DATE', 'TIMESTAMP', 'CHAR', 'VARCHAR2',
    'NCHAR', 'NVARCHAR2')
) a
cross join xmltable (
  '/ROWSET/ROW'
  passing xmltype(a.xml_clob)
  columns distinct_count number path 'C1',
    null_count number path 'C2'
) x;

OWNER           TABLE_NAME      COLUMN_NAME     DISTINCT_COUNT NULL_COUNT
--------------- --------------- --------------- -------------- ----------
MY_SCHEMA       T42             ID                           3          0
MY_SCHEMA       T42             STR                          2          1