打印 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
。你用 oracle11g
和 oracle10g
标记了你的 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
我有一个 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
。你用 oracle11g
和 oracle10g
标记了你的 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