这可以仅使用普通 SQL(无过程或函数等)来完成,例如使用 MERGE 语句。一个完整的会话,从设置表格开始,插入值,为 cmets 多添加一列,然后是 MERGE 语句,结果“之前”和“之后”如下所示。
我又添加了一行数据,其中没有 NULL 值。您总是需要这样的东西来测试解决方案:当连续有 no NULL 值时它是否正常工作?
真正的工作是在下面的解决方案中标记为x 的子查询中完成的。首先我 unpivot 原始表中的数据(添加 ROWID 以便我以后可以识别每一行)。 UNPIVOT 需要 Oracle 11.1 或更高版本;我还使用需要 Oracle 11.2 或更高版本的 LISTAGG。你用oracle11g 和oracle10g 标记了你的帖子,所以我真的不知道你有什么,无论如何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