Oracle:使用分隔符连接,但前提是两个操作数都不为空
Posted
技术标签:
【中文标题】Oracle:使用分隔符连接,但前提是两个操作数都不为空【英文标题】:Oracle: Concat with delimiter, but only if both operands are NOT NULL 【发布时间】:2012-07-12 08:08:21 【问题描述】:我想选择几个字段的串联,但它们之间有一个分隔符。只有当两个操作数都不为空时,分隔符才应该存在。
所以对于a='foo', b=NULL, c='bar'
的记录,我想得到abc='foo;bar'
的结果(而不是'foo;;bar'
)。
我想要一个像concat_sep(a, b, ';')
这样只添加';'的函数如果 a 和 b 都不为 null,则介于两者之间。
当然,我可以像这样使用 nvl2:
select
a, b, c,
substr(abc, 1, length(abc) - 1) as abc
from
(select
a, b, c,
nvl2(a, a || ';', '') || nvl2(b, b || ';', '') || nvl2(c, c || ';', '') as abc
from
Table1)
但是正如您所看到的,这段代码很快就会变得阻塞,尤其是当您有超过 3 列并且您给它们取了合理的名称而不是 a、b 和 c 时。 ;-)
我找不到更短、更容易或更易读的方法,但我想在完全放弃之前我会在这里问一下(或者自己浪费时间编写这样的函数)。
【问题讨论】:
似乎是您想要的非常具体的逻辑:为什么自己编写自己的函数会浪费时间? 如果事实证明已经有一个就好了。 :) 没有 11g listagg 看起来您需要自己编写。看看你的 cmets,似乎你自己写的,所以我很困惑,你在寻找一些你自己的函数不提供的功能吗?也许是一个用例示例,看看您打算如何使用它(我可以想到几种方法) 我实际上并不需要 LISTAGG,我需要一个 CONCAT 函数,它允许我指定一个分隔符来放置在两个(或多个)非空值之间。 @LukasEder 建议将 LISTAGG 作为一种可能的解决方案,考虑到我的用例,这是一个相当复杂的解决方案。所以我确实有我的 LISTAGG 替代品,但这不是我现在需要的。 【参考方案1】:我知道你使用的是 10g,所以这行不通。但为了完整起见,LISTAGG()
“正确”处理NULL
值。不过,为此您必须更新到 11g2:
-- Some sample data, roughly equivalent to yours
with t as (
select 'foo' as x from dual union all
select null from dual union all
select 'bar' from dual
)
-- Use the listagg aggregate function to join all values
select listagg(x, ';') within group (order by rownum)
from t;
或者更简洁一点,如果您想列出表中的列:
-- I use SYS.ORA_MINING_VARCHAR2_NT as a TABLE TYPE. Use your own, if you prefer
select listagg(column_value, ';') within group (order by rownum)
from table(ORA_MINING_VARCHAR2_NT('foo', null, 'bar'));
或针对实际表格:
select listagg(column_value, ';')
within group (order by rownum)
from Table1
cross join table(ORA_MINING_VARCHAR2_NT(Table1.a, Table1.b, Table1.c))
group by Table1.id;
现在我不确定这是否比你原来的例子更好(更易读):-)
【讨论】:
将列转换为表然后聚合它感觉有点恶心。我想尝试一下,至少作为一个学习示例。 :) 太糟糕了 ORA_MINING_VARCHAR2_NT 在 10g 中似乎也没有,这很遗憾,因为我已经写了一个 LISTAGG 替代 10g:***.com/a/7885793/511529 是的,icky 只是一个开始。它曾经被称为DMSYS.ORA_MINING_VARCHAR2_NT
。 This answer 展示了如何找到另一个可以满足您需求的 SYS
table/varray 类型
也不起作用。显然我的数据库中还没有数据挖掘功能。我会记住这一点,但我觉得无论如何我都不应该在生产代码中使用这个解决方案。 ;-)
是的,请不要。至少为了你的程序员同行 :-)
我使用了自己的 nvl2 解决方案,如果我将所有 nvl2 放在单独的行上,它实际上看起来相当不错。不过,我很乐意接受这个答案。它对我不起作用,但如果你有合适的数据库,它可能会起作用。这让我记得更频繁地跳出框框思考。 :)【参考方案2】:
select trim(';' from REGEXP_REPLACE (a || ';' || b || ';' || c , ';+' , ';')) abc
from Table1
【讨论】:
您好,欢迎来到 SO,最好详细说明您的答案:***.com/help/how-to-answer【参考方案3】:AFAIK,没有简洁的方法可以做到这一点。
过去,我已经诉诸于
SELECT a
|| DECODE(b
, NULL, NULL
, ';' || b)
|| DECODE(c
, NULL, NULL
, ';' || c)
|| DECODE(d
, NULL, NULL
, ';' || d)
...
FROM table1
但这并不比你的例子更好。
【讨论】:
确实,差不多。还是谢谢你。以上是关于Oracle:使用分隔符连接,但前提是两个操作数都不为空的主要内容,如果未能解决你的问题,请参考以下文章
Oracle REGEXP_SUBSTR |获取两个分隔符之间的字符串