打印 NULL 值列名称

Print NULL Value column Name

我有一个 table 列,如下所示。我需要检查每条记录并在另一个名为 Comments

的现有列中显示具有 NULL 值的列名
ColA   ColB    ColC    ColD   ColE   Comments
----   ----    -----   ----   ----   ------------------------------
1      7       3       4      NULL   NULL VALUE In ColE      
2      9       NULL    12     NULL   NULL VALUE in ColC, ColE   
3      NULL    NULL    NULL   10     NULL VALUE IN ColB, Colc, ColD 

输出应类似于上述结果集中的 Comments 列。

这是你想要的代码吗?

declare 
  v_table_name  varchar2(31)  := 'MYTABLE';
  v_sql         varchar2(4000);
  v_columnName  varchar2(100);
  v_col         pls_integer; 
  cm            varchar2(1); 
  i             pls_integer := 0;   
begin
for b in ( select rownum rnum, t.rowid rid, t.* from mytable t order by rownum )
loop
 begin
 for c in ( select t.* from user_tab_columns t where t.table_name = v_table_name order by column_name ) 
 loop    
   v_sql := 'select ' || c.column_name  || ' from ' || v_table_name || ' t where t.rowid = '''||b.rid||'''';
   execute immediate v_sql into v_col; 
   if nvl(v_col,0)=0 then     
     if i>0 then cm:=','; end if;
     v_columnName := v_columnName||cm||c.column_name; 
     i:=+1;
   end if; 
 end loop;
    dbms_output.put_line(rpad(nvl(to_char(b.rnum),'NULL'),5,' ')||' '||rpad(nvl(to_char(b.cola),'NULL'),5,' ')||' '||rpad(nvl(to_char(b.colb),'NULL'),5,' ')||' '||rpad(nvl(to_char(b.colc),'NULL'),5,' ')||' '||rpad(nvl(to_char(b.cold),'NULL'),5,' ')||' '||rpad(nvl(to_char(b.cole),'NULL'),5,' ')||' '||'NULL VALUE In '||v_columnName); 
    v_columnName := null; i := 0; cm := null;
  exception when no_data_found then continue;
 end; 
end loop; 
end; 

通过将 colA - E 视为数字类型并且注释在 mytable 中不存在。

这可以只使用简单的 SQL(没有过程或函数等)来完成,例如使用 MERGE 语句。一个完整的会话,从设置 table 开始,插入值,再添加一列用于注释,然后是 MERGE 语句,然后显示结果 "before" 和 "after"下面。

我又添加了一行数据,其中没有 NULL 个值。您总是需要这样的东西来测试解决方案:当连续 no NULL 值时它是否正常工作?

真正的工作是在下面的解决方案中标记为 x 的子查询中完成的。首先,我 unpivot 来自原始 table 的数据(添加 ROWID 以便稍后识别每一行)。 UNPIVOT 需要 Oracle 11.1 或更高版本;我还使用需要 Oracle 11.2 或更高版本的 LISTAGG。你用 oracle11goracle10g 标记了你的 post 所以我真的不知道你有什么,无论如何 11g 等等都是营销名称;正确的版本是 11.1 或 11.2(或者,实际上是 11.2.0.4.0 和类似版本)。您可以通过 select * from v$version.

查看您的版本

如果您没有 Oracle 11.2,则可以使用其他工具(分层查询或 XMLAGG 等)完成字符串聚合 - Stack Overflow 充满了关于如何完成的问题。同样,如果您没有 Oracle 11.1,则可以使用交叉连接来完成反透视;我没有检查,但我认为 Stack Overflow 上也有很多关于此的问题。这两种操作都是标准的,与您的问题无关;如果您需要帮助,请先搜索此站点,如果您 运行 遇到困难,请回信。

所以,回到我的方法。我逆透视数据,跟踪 rowid。在 unpivot 操作中,我包含空值(这不是默认值,因此需要 include nulls)。然后从结果中我只保留值为 NULL 的行。然后我按 RID 分组并用 LISTAGG 聚合。这为 MERGE 操作准备子查询 x,这是超出这一点的标准。

关于效率的注意事项 我在评论中询问了 OP 是否应该首选快速执行或简单的解决方案(更容易维护)。 OP 表示易于维护更为重要。下面的解决方案更清晰,但由于以下原因而效率不高。在原来的 table 中,五列中的值已经 "grouped" 因为它们出现在同一行中。 Unpivoting 会丢失此信息;然后我们必须再次按 ROWID 分组,以创建 LISTAGG 字符串并为 MERGE 做准备。 GROUP BY 操作开销很大,如果我们编写的代码不分解输入行,则不需要它。但是编写一个不分解输入行的解决方案虽然可能,但会更加混乱,尤其是随着列数的增加。

设置

create table tbl(cola number, colb number, colc number, cold number,cole number);
insert into tbl(cola, colb, colc, cold, cole) values (1,    7,    3,    4, NULL);  
insert into tbl(cola, colb, colc, cold, cole) values (2,    9, NULL,   12, NULL);
insert into tbl(cola, colb, colc, cold, cole) values (3, NULL, NULL, NULL,   10);
insert into tbl(cola, colb, colc, cold, cole) values (0,    1,    2,    3,    5);
commit;

alter table tbl add (comment_about_nulls varchar2(1000));

select * from tbl;

COLA COLB COLC COLD COLE COMMENT_ABOUT_NULLS                    
---- ---- ---- ---- ---- ----------------------------------------
   1    7    3    4                                              
   2    9        12                                              
   3                  10
   0    1    2    3    4

解决方案

merge into tbl
using (
        select   rid, listagg(colname, ', ') within group (order by colname) str
        from     (select rowid as rid, cola, colb, colc, cold, cole from tbl)
        unpivot  include nulls (val for colname in 
                          (cola as 'ColA', colb as 'ColB', colc as 'ColC',
                                           cold as 'ColD', cole as 'ColE'))
        where    val is null
        group by rid
      ) x
  on (tbl.rowid = x.rid)
when matched then update
  set tbl.comment_about_nulls = 'NULL VALUE IN ' || x.str
;

select * from tbl;

COLA COLB COLC COLD COLE COMMENT_ABOUT_NULLS                    
---- ---- ---- ---- ---- ----------------------------------------
   1    7    3    4      NULL VALUE IN ColE                      
   2    9        12      NULL VALUE IN ColC, ColE                
   3                  10 NULL VALUE IN ColB, ColC, ColD          
   0    1    2    3    5