Aqui está uma solução que usa apenas funções de string padrão (em vez de expressões regulares) - o que deve resultar em execução mais rápida na maioria dos casos; remove 3 somente quando é o primeiro caractere seguido de vírgula, o último caractere precedido de vírgula, ou precedido e seguido de vírgula, e remove a vírgula que o precede no caso do meio e remove a vírgula que o segue no primeiro e terceiro caso.
Ele é capaz de remover dois 3's seguidos (o que algumas das outras soluções oferecidas não são capazes de fazer) enquanto deixa vírgulas consecutivas no lugar (que presumivelmente representam NULL) e não perturba números como 38 ou 123.
A estratégia é primeiro dobrar cada vírgula (substituir
,
com ,,
) e acrescentar e preceder uma vírgula (no início e no final da string). Em seguida, remova todas as ocorrências de ,3,
. Do que resta, substitua cada ,,
de volta com um único ,
e, finalmente, remova o ,
inicial e final . 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.
Observação Conforme apontado por MT0 (veja Comentários abaixo), isso cortará muito se a string original começar ou terminar com vírgulas. Para cobrir esse caso, em vez de envolver tudo dentro de
trim(both ',' from ...)
Eu deveria envolver o resto em uma subconsulta e usar algo como substr(new_str, 2, length(new_str) - 2)
na consulta externa.