Ecco una soluzione che utilizza solo funzioni di stringa standard (piuttosto che espressioni regolari), il che dovrebbe comportare un'esecuzione più rapida nella maggior parte dei casi; rimuove 3 solo quando è il primo carattere seguito da virgola, l'ultimo carattere preceduto da virgola, oppure preceduto e seguito da virgola, rimuove la virgola che lo precede al centro e rimuove la virgola che lo segue nel primo e terzo caso.
È in grado di rimuovere due 3 di fila (cosa che alcune delle altre soluzioni offerte non sono in grado di fare) lasciando al loro posto virgole consecutive (che presumibilmente stanno per NULL) e non disturbano numeri come 38 o 123.
La strategia consiste nel raddoppiare prima ogni virgola (sostituire ,
con ,,
) e aggiungere e anteporre una virgola (all'inizio e alla fine della stringa). Quindi rimuovi ogni occorrenza di ,3,
. Da ciò che è rimasto, sostituisci ogni ,,
indietro con un singolo ,
e infine rimuovere il ,
iniziale e finale .
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.
Nota Come sottolineato da MT0 (vedi Commenti sotto), questo taglierà troppo se la stringa originale inizia o finisce con virgole. Per coprire quel caso, invece di avvolgere tutto all'interno di trim(both ',' from ...)
Dovrei racchiudere il resto in una sottoquery e usare qualcosa come substr(new_str, 2, length(new_str) - 2)
nella query esterna.