【问题标题】:Print NULL Value column Name打印 NULL 值列名称
【发布时间】:2017-12-20 07:04:23
【问题描述】:

我有一张表,其中包含 ColA、...、ColE 列,如下所示。我需要检查每条记录并在另一个名为 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 列。

【问题讨论】:

  • 对您来说更重要的是:代码简单(易于维护)还是效率(执行速度)?不要说两者!解决这个问题的方法是有效率的,但是代码比较麻烦。
  • 请澄清:您所说的现有列是什么意思?您的表(保存在磁盘上)是否有这样的列?还是您只需要SELECT 查询的输出中的那一列? (相对于:该列确实存在,您需要UPDATE 它并将结果保存在磁盘上。)
  • 我创建了一个名为 Comments 的列 - 如果任何行具有 null 并且该列名应在 Comments 列中更新为 - ColE 中的 NULL 值....如果更多列那么它应该所有这些列
  • 更多关于代码的简单性......

标签: oracle oracle11g oracle10g oracle-sqldeveloper


【解决方案1】:

这可以仅使用普通 SQL(无过程或函数等)来完成,例如使用 MERGE 语句。一个完整的会话,从设置表格开始,插入值,为 cmets 多添加一列,然后是 MERGE 语句,结果“之前”和“之后”如下所示。

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

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

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

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

关于效率的说明我在评论中询问了 OP 是否应该首选快速执行或解决方案的简单性(更易于维护)。 OP说易于维护更重要。下面的解决方案更清晰,但由于以下原因效率不高。在原始表中,五列中的值已经按它们出现在同一行中的事实“分组”。取消透视会丢失此信息;然后我们必须再次按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  

【讨论】:

  • 谢谢我的同事(数学家),我很欣赏你的解决方案。
  • @mathguy - 非常感谢,它按照我的要求工作。感谢您的时间和帮助
【解决方案2】:

这是你想要的代码吗?

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 视为数字类型并且 cmets 在 mytable 中不存在。

【讨论】:

  • ColA ColB ColC ColD ColE ---- ---- ----- ---- ---- 1 7 3 4 NULL 2 9 NULL 12 NULL 3 NULL NULL NULL 10 我的结果应如下所示,请不要列同时包含数字和 Varchar 值 结果 NULL VALUE In ColE NULL VALUE in ColC, ColE NULL VALUE IN ColB, Colc, ColD
  • 感谢您的回复。下面是我期望的输出 ColA ColB ColC ColD ColE ---- ---- ----- ---- ---- 1 7 3 4 NULL 2 9 NULL 12 NULL 3 NULL NULL NULL 10 My结果应如下所示,请不要列同时包含数字和 Varchar 值 结果 NULL VALUE In ColE NULL VALUE in ColC, ColE NULL VALUE IN ColB, Colc, ColD
  • part " ...'NULL VALUE In '||v_columnName" 已经代表这个( cmets )。你试过代码,真的吗?只是,我没有为标题部分添加任何代码。
  • 这就是我获得输出的方式。请注意我仍然是 SQL 的新手 1 1 7 3 4 NULL NULL VALUE In 2 2 9 NULL 12 NULL NULL VALUE In 3 3 NULL NULL NULL 10 NULL VALUE In
  • 需要一个小帮助,如何将输出更新到 My_table 相应的记录结果列。
猜你喜欢
  • 1970-01-01
  • 2010-11-12
  • 2015-08-11
  • 1970-01-01
  • 2021-08-10
  • 1970-01-01
  • 1970-01-01
  • 2019-08-06
相关资源
最近更新 更多