计算 Oracle 表中空值的数量?

Posted

技术标签:

【中文标题】计算 Oracle 表中空值的数量?【英文标题】:Count the number of null values into an Oracle table? 【发布时间】:2017-06-08 18:29:18 【问题描述】:

我需要计算Oracle 中一个表中所有列的空值个数。

例如,我执行以下语句来创建表 TEST 并插入数据。

 CREATE TABLE TEST
 (  A VARCHAR2(20 BYTE), 
    B VARCHAR2(20 BYTE), 
    C VARCHAR2(20 BYTE)
  );
Insert into TEST (A) values ('a');
Insert into TEST (B) values ('b');
Insert into TEST (C) values ('c');

现在,我编写以下代码来计算表 TEST 中空值的数量:

declare 
cnt number :=0;
temp number :=0;
begin
  for r in ( select column_name, data_type
             from    user_tab_columns 
             where table_name = upper('test')
             order by column_id )
  loop
      if r.data_type <> 'NOT NULL' then
         select count(*) into temp FROM TEST where r.column_name IS NULL;
         cnt := cnt + temp;
      END IF;
  end loop;
   dbms_output.put_line('Total: '||cnt);
end;
/

当期望值为 6 时返回 0。

哪里出错了?

提前致谢。

【问题讨论】:

在单列还是所有列中? Select sum(case when col1 is null then 1 else 0 end + case when col2 is null then 1 else 0 end + case when col3 is null then 1 else 0 end + ....) as NullValues from table 嗨,xQbert,在所有列中! 您不能在普通 SQL 查询中使用列名作为参数。为此,您可以执行动态 SQL。 docs.oracle.com/cloud/latest/db112/LNPLS/dynamic.htm#LNPLS011 如果你可以用纯 sql 解决问题,你应该喜欢这种方式而不是过程代码。 @jackattack 您提出的使用纯 SQL 的解决方案没有回答我的问题。我需要一个空值的总数。那就是 sum(col1_nulls, col2_nulls, ...., colN_nulls); 【参考方案1】:

计算每列的 NULL 数

为了计算表 T所有列 的 NULL 值,您可以运行

SELECT COUNT(*) - COUNT(col1) col1_nulls
     , COUNT(*) - COUNT(col2) col2_nulls
     ,..
     , COUNT(*) - COUNT(colN) colN_nulls
     , COUNT(*) total_rows
FROM   T
/

其中 col1, col2, .., colN 应替换为 T 表的实际列名。

聚合函数 - 如 COUNT()- 忽略 NULL 值,因此 COUNT(*) - COUNT(col) 将为您提供每列有多少个空值。

汇总表的所有 NULL

如果你想知道有多少字段是NULL,我的意思是你可以知道每条记录的每个NULL

WITH d as (    
    SELECT COUNT(*) - COUNT(col1) col1_nulls
         , COUNT(*) - COUNT(col2) col2_nulls
         ,..
         , COUNT(*) - COUNT(colN) colN_nulls
         , COUNT(*) total_rows
    FROM   T
) SELECT col1_nulls + col1_nulls +..+ colN_null
  FROM d 
/

汇总表的所有 NULL(使用 Oracle 字典表)

以下是一项改进,您现在只需要表名,并且基于它编写函数非常容易

DECLARE
  T    VARCHAR2(64) := '<YOUR TABLE NAME>';
  expr VARCHAR2(32767);
  q    INTEGER;
BEGIN
  SELECT 'SELECT /*+FULL(T) PARALLEL(T)*/' || COUNT(*) || ' * COUNT(*) OVER () - ' || LISTAGG('COUNT(' || COLUMN_NAME || ')', ' + ') WITHIN GROUP (ORDER BY COLUMN_ID) || ' FROM ' || T
  INTO   expr
  FROM   USER_TAB_COLUMNS
  WHERE  TABLE_NAME = T;

  -- This line is for debugging purposes only
  DBMS_OUTPUT.PUT_LINE(expr);

  EXECUTE IMMEDIATE expr INTO q;

  DBMS_OUTPUT.PUT_LINE(q);
END;
/

由于计算意味着全表扫描,expr 变量中生成的代码已针对并行运行进行了优化。

用户定义函数null_fields

函数版本,还包括一个可选参数,以便能够在其他模式上运行。

CREATE OR REPLACE FUNCTION null_fields(table_name IN VARCHAR2, owner IN VARCHAR2 DEFAULT USER)
  RETURN INTEGER IS
  T    VARCHAR2(64) := UPPER(table_name);
  o    VARCHAR2(64) := UPPER(owner);
  expr VARCHAR2(32767);
  q    INTEGER;
BEGIN
  SELECT 'SELECT /*+FULL(T) PARALLEL(T)*/' || COUNT(*) || ' * COUNT(*) OVER () - ' || listagg('COUNT(' || column_name || ')', ' + ') WITHIN GROUP (ORDER BY column_id) || ' FROM ' || o || '.' || T || ' t'
  INTO   expr
  FROM   all_tab_columns
  WHERE  table_name = T;

  EXECUTE IMMEDIATE expr INTO q;

  RETURN q;
END;
/

-- Usage 1
SELECT null_fields('<your table name>') FROM dual
/

-- Usage 2
SELECT null_fields('<your table name>', '<table owner>') FROM dual
/

【讨论】:

【参考方案2】:

谢谢@彼得勋爵:

以下 PL/SQL 脚本有效

declare 
cnt number :=0;
temp number :=0;
begin
  for r in ( select column_name, nullable
             from    user_tab_columns 
             where table_name = upper('test')
             order by column_id )
  loop
      if r.nullable = 'Y' then
         EXECUTE IMMEDIATE 'SELECT count(*) FROM test where '|| r.column_name ||' IS NULL' into temp ;
         cnt := cnt + temp;
      END IF;
  end loop;
   dbms_output.put_line('Total: '||cnt);
end;
/

表名test可以替换成您感兴趣的表名。

我希望这个解决方案有用!

【讨论】:

NOT NULL 不是数据类型。您应该尝试一次性完成此操作,而不是为每一列扫描一次表格。 谢谢@William Robertson,你是对的。我刚刚修复了代码。 太好了,这是一个改进。不过,它仍然会在每个非强制性列中对表进行一次全扫描。【参考方案3】:

你执行的动态SQL(这是EXECUTE IMMEDIATE中使用的字符串)应该是

select sum(
    decode(a,null,1,0)
    +decode(b,null,1,0)
    +decode(c,null,1,0)
    ) nullcols
from test;

每个加法对应一个 NOT NULL 列。

这里只需要一次表扫描就可以得到结果。

【讨论】:

【参考方案4】:

使用数据字典几乎可以立即找到NULL 值的数量:

select sum(num_nulls) sum_num_nulls
from all_tab_columns
where owner = user
    and table_name = 'TEST';

SUM_NUM_NULLS
-------------
6

只有在最近收集了优化器统计信息并且使用样本大小的默认值收集时,这些值才会正确。

这些可能看起来很重要,但无论如何熟悉数据库的统计信息收集过程是值得的。如果您的数据库没有自动收集统计信息,或者如果您的数据库没有使用默认样本大小,那么这些可能是您需要注意的大问题。

要手动收集特定表的统计信息,可以使用如下语句:

begin
    dbms_stats.gather_table_stats(user, 'TEST');
end;
/

【讨论】:

【参考方案5】:
select COUNT(1) TOTAL from table where COLUMN is NULL;

【讨论】:

COL = NULL 不正确。 COL IS NULL 确实有效 什么是count(1)?标准 SQL 表达式为count(*) @YenierTorres 感谢您的更正 COL 是 NULL 它应该是(更正)。谢谢 @WilliamRobertson 它可以是任何东西,我习惯 COUNT(1) 或 COUNT(COL_NAME) 都给出正确的结果。谢谢 @Gurdyal: count(*)count(1) 确实是一回事。然而 count(*)count(col_name)同一件事

以上是关于计算 Oracle 表中空值的数量?的主要内容,如果未能解决你的问题,请参考以下文章

用 pentaho 计算每列空值的数量

Pyspark - 计算每个数据框列中的空值数量

对于表中的每个观测值,根据纬度和经度 (R) 计算 x 米内的表中其他观测值的数量

逐行计算pyspark数据帧中的空值数

ORACLE 按表字段值的不同统计数量

计算Google工作表列表中的所有唯一值+其总数量和总价格