【问题标题】:Oracle: Count non-null fields for each column in a tableOracle:计算表中每一列的非空字段
【发布时间】:2021-04-27 21:07:26
【问题描述】:

我需要一个查询来计算表中每个列的非空值的总数。由于我的表有数百列,因此我正在寻找一种只需要输入表名的解决方案。

也许使用以下结果:

select COLUMN_NAME from ALL_TAB_COLUMNS where TABLE_NAME='ORDERS';

获取列名,然后使用子查询对每个列名进行计数?额外的复杂情况是我只有对数据库的只读访问权限,因此我无法创建任何临时表。

这个有点不合我意,所以感谢任何帮助。

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    用 SQL 或使用电子表格构建查询。然后运行查询。

    例如,假设您的列名很简单并且没有特殊字符:

    select replace('select ''[col]'', count([col]) from orders union all ',
                   '[col]', COLUMN_NAME
                  ) as sql
    from ALL_TAB_COLUMNS
    where TABLE_NAME = 'ORDERS';
    

    (当然,这可以适应更复杂的列名,但我试图展示这个想法。)

    然后复制代码,去掉最后的union all并运行。

    如果没有太多列,可以将其放在一个字符串中:

    select listagg(replace('select ''[col]'', count([col]) from orders',
                           '[col]', COLUMN_NAME
                          ), ' union all '
                   ) within group (order by column_name) as sql
    from ALL_TAB_COLUMNS
    where TABLE_NAME = 'ORDERS';
    

    您也可以通过相同的查询使用execute immediate,但这似乎有点过头了。

    【讨论】:

    • 这对于任何区分大小写的列名或包含非字母数字字符(逗号、引号、括号等)的列名都将失败。
    【解决方案2】:

    如果您对结果感到满意 row-ar 而不是 column-ar:

    SELECT 'SELECT ''dummy'', 0 FROM DUAL' FROM DUAL
    UNION ALL 
    SELECT 
      ' UNION ALL SELECT ''' || 
      column_name ||
      ''', COUNT(' || 
      column_name || 
      ') FROM ' || 
      TABLE_NAME
    FROM 
      all_tab_columns
    WHERE 
      table_name = 'ORDERS'
    

    这是一个“编写 SQL 的 SQL”,您可以复制并运行它来获得答案。应该制作一个如下所示的结果集:

    SELECT 'dummy', 0 FROM dual

    UNION ALL SELECT 'col1', COUNT(col1) FROM ORDERS

    UNION ALL SELECT 'col2', COUNT(col2) FROM ORDERS

    ...

    如果你想要你的结果列-ar:

    SELECT 'SELECT '
    UNION ALL
    SELECT 
      'COUNT(' || 
      column_name ||
      ') as count_' || 
      column_name || 
      ', ' || 
      TABLE_NAME
    FROM 
      all_tab_columns
    WHERE 
      table_name = 'ORDERS'
    UNION ALL
    SELECT 'null as dummy_column FROM ORDERS'
    

    应该创建一个如下所示的结果集:

    选择

    COUNT(col1) as count_col1,

    COUNT(col2) as count_col2,

    ...

    null as dummycoll FROM orders

    警告:我没有在任何可以测试这些的地方安装 oracle,它是从内存中写入的,可能需要一些调试

    【讨论】:

    • 这对于任何区分大小写的列名或包含非字母数字字符(逗号、引号、括号等)的列名都将失败。
    • 将其称为读者练习。我知道人们有时会问这样,因为他们需要用勺子喂食,但到底是什么?有时你只需要抓住机会,让人们能够为自己做一些基本的事情,比如“吸气,呼气”——这并不是说他们不会得到一个明智的错误信息来指导他们
    【解决方案3】:

    这将生成 SQL 以获取列中的计数,并将处理区分大小写的列名和包含非字母数字字符的列名:

    SELECT 'SELECT '
           || LISTAGG(
             'COUNT("' || column_name || '") AS "' || column_name || '"',
             ', '
           ) WITHIN GROUP ( ORDER BY column_id )
           || ' FROM "' || table_name || '"' AS sql
    FROM   ALL_TAB_COLUMNS
    WHERE  TABLE_NAME = 'ORDERS'
    GROUP BY TABLE_NAME;
    

    或者,如果您有大量列生成超过 4000 个字符的字符串,您可以使用 custom aggregation function to aggregate VARCHAR2s into a CLOB,然后执行以下操作:

    SELECT 'SELECT '
           || CLOBAgg( 'COUNT("' || column_name || '") AS "' || column_name || '"' )
           || ' FROM "' || table_name || '"' AS sql
    FROM   ALL_TAB_COLUMNS
    WHERE  TABLE_NAME = 'ORDERS'
    GROUP BY TABLE_NAME;
    

    【讨论】:

    • ORA-01489: 字符串拼接结果太长 01489. 00000 - “字符串拼接结果太长” *原因:字符串拼接结果超过最大大小。 *操作:确保结果小于最大大小。
    • @h0dges Create a user defined aggregation function to generate a CLOB 并使用CLOBAGG( ... ) 而不是LISTAGG( .. ) WITHIN GROUP ( ... )
    【解决方案4】:

    在 Oracle 19 中(我在 Ora 12 中使用了类似的代码,也许也可以),这无需生成另一个选择来执行:

    select * from
    (
        select table_name, column_name,
               to_number( extractvalue( xmltype(dbms_xmlgen.getxml('select count(to_char(substr('||column_name||',1,1))) c from '||table_name)) ,'/ROWSET/ROW/C')) count
        from all_tab_columns where owner = user
    ) 
    --where table_name = 'MY_TABLE'
    ;
    

    它将创建带有计数的 XML,从中提取当前计数。这里的 substr 和 to_char 函数用于提取第一个字符,因此它也适用于 CLOB 列

    【讨论】:

      猜你喜欢
      • 2011-11-05
      • 1970-01-01
      • 1970-01-01
      • 2011-08-29
      • 1970-01-01
      • 2012-01-31
      • 2011-06-24
      • 1970-01-01
      • 2013-08-14
      相关资源
      最近更新 更多