如何从oracle中的逗号分隔字符串中删除特定值

时间:2017-02-24 16:33:12

标签: sql oracle oracle11g

我想使用oracle从逗号分隔的sting中删除特定值。

示例输入 -

col
1,2,3,4,5

假设我想从字符串中删除3。

示例输出 -

col
1,2,4,5

请使用oracle查询建议我如何做到这一点。

感谢。

5 个答案:

答案 0 :(得分:4)

这是一个只使用标准字符串函数(而不是正则表达式)的解决方案 - 在大多数情况下应该可以更快地执行;只有当第一个字符后跟逗号,最后一个字符前面有逗号,或者前面跟着逗号后才删除3,并删除中间情况下它前面的逗号,并删除后面的逗号。第一和第三种情况。

它可以连续删除两个3(其他一些解决方案无法提供),同时留下连续的逗号(可能代表NULL)并且不会干扰38或123之类的数字

策略是首先将每个逗号加倍(用,替换,,)并附加并添加逗号(到字符串的开头和结尾)。然后删除,3,的每次出现。从剩下的内容中,将每个,,替换为一个,,最后删除前导和尾随,

with
     test_data ( str ) as (
       select '1,2,3,4,5'     from dual union all
       select '1,2,3,3,4,4,5' from dual union all
       select '12,34,5'       from dual union all
       select '1,,,3,3,3,4'   from dual
     )
select str,
       trim(both ',' from 
             replace( replace(',' || replace(str, ',', ',,') || ',', ',3,'), ',,', ',')
           ) as new_str
from   test_data
;

STR           NEW_STR
------------- ----------
1,2,3,4,5     1,2,4,5
1,2,3,3,4,4,5 1,2,4,4,5
12,34,5       12,34,5
1,,,3,3,3,4   1,,,4

4 rows selected.

注意正如MT0所指出的(参见下面的评论),如果原始字符串以逗号开头或结尾,这将修剪得太多。为了覆盖这种情况,我应该将其余内容包装在子查询中,而不是将所有内容包装在子查询中,并在外部查询中使用类似trim(both ',' from ...)的内容。

答案 1 :(得分:3)

这是一种方法:

select trim(both ',' from replace(',' || '1,2,3,4,5' || ',', ',' || '3' || ',', ','))

也就是说,存储以逗号分隔的字符串是一个非常非常糟糕的主意。几乎没有理由做这样的事情。 Oracle支持JSON,XML和嵌套表 - 所有这些都是更好的选择。

删除元素的需要表明数据设计不佳。

答案 2 :(得分:1)

您可以使用XMLTABLE转换列表行,过滤以删除不需要的行,然后重新聚合它们:

SELECT LISTAGG( x.value.getStringVal(), ',' ) WITHIN GROUP ( ORDER BY idx )
FROM   XMLTABLE(
         ( '1,2,3,4,5' )
         COLUMNS value XMLTYPE PATH '.',
                 idx   FOR ORDINALITY
       ) x
WHERE  x.value.getStringVal() != 3;

对于一个简单的过滤器,这可能不值得,你应该使用类似的东西(基于@ mathguy的solution):

SELECT SUBSTR( new_list, 2, LENGTH( new_list ) - 2 ) AS new_list
FROM   (
  SELECT REPLACE(
           REPLACE(
             ',' || REPLACE( :list, ',', ',,' ) || ',',
             ',' || :value_to_replace || ','
           ),
           ',,',
           ','
         ) AS new_list
  FROM   DUAL
)

但是,如果过滤更复杂,那么将列表转换为行,过滤和重新聚合可能是值得的。

答案 3 :(得分:0)

我不知道如何在Oracle中执行此操作,但使用SQL-Server我会使用一个技巧:

  • 通过将逗号替换为标记
  • 将列表转换为XML
  • 使用XQuery过滤数据
  • reconcatenate

这是SQL Server语法,但可能指向您的方向:

declare @s varchar(100)='1,2,2,3,3,4';
declare @exclude int=3;

WITH Casted AS
(
    SELECT CAST('<x>' + REPLACE(@s,',','</x><x>') + '</x>' AS XML) AS TheXml
)
SELECT x.value('.','int')
FROM Casted
CROSS APPLY TheXml.nodes('/x[text()!=sql:variable("@exclude")]') AS A(x)

更新

I just found this answer似乎很好地展示了如何开始......

答案 4 :(得分:0)

我同意Gordon的观点,即在列中存储逗号分隔的数据是一个非常糟糕的主意。

我只是在csv之前加上&#39;&#39;,然后使用替换功能,然后使用左侧修剪功能来清理前面的&#39;,&#39;。

SCOTT@tst>VAR b_number varchar2(5);
SCOTT@tst>EXEC :b_number:= '3';

PL/SQL procedure successfully completed.

SCOTT@tst>WITH srce AS (
  2      SELECT
  3          ',' || '3,1,2,3,3,4,5,3' col
  4      FROM
  5          dual
  6  ) SELECT
  7      ltrim(replace(col,',' ||:b_number),',') col
  8  FROM
  9      srce;
COL      
1,2,4,5